简体   繁体   中英

How to call type conversion operator in C# generic class?

It seems we cannot call type conversion operator easily in C# generic class. Here is the code. Why?

T006 finally archive our target.

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Linq;

namespace ConsoleApplication1
{
    class vec<T, T2> : List<T> where T : class
    {
        public vec(IEnumerable<T2> other)
        {
            //Converter<T2, T> cvt = (v) => (T)v; // T004 failed, try defined function dynamicly, cannot compile, too.

            // T006 pass, client happy, we not happy, but anyway passed, performance may not happy.
            var conversionOperator = typeof(T).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                    .Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
                                    .Where(m => m.ReturnType == typeof(T))
                                    .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T2))
                                    .FirstOrDefault();
            Func<T2, T> cvt = (obj) =>
            {
                if (conversionOperator != null)
                    return (T)conversionOperator.Invoke(null, new object[] { obj });
                else
                    return default(T);
            };

            foreach (T2 item in other)
            {
                //Add((T)item);         // T001 failed, this line cannot compile
                //Add(item as T);       // T002 failed, this line alwasy return null. //  http://msdn.microsoft.com/en-us/library/vstudio/cscsdfbt.aspx
                //Add((T)(object)item); // T003 failed, pass compile, but throw exception at runtime.
                Add(cvt(item));         // T006 pass.
            }
        }

        // T005 pass, but clients for this code will not happy.
        public vec(Converter<T2, T> cvt, IEnumerable<T2> other)
        {
            foreach (T2 item in other)
            {
                Add(cvt(item));
            }

        }
    }

    class XXX
    {
        public int foo = 22;
        static public explicit operator XXX(YYY other)
        {
            XXX me = new XXX();
            me.foo = (int)other.foo;
            return me;
        }
    }

    class YYY
    {
        public float foo = 11;
    }



    class Program
    {
        static void Main(string[] args)
        {
            YYY[] from = new YYY[2];
            for (int i = 0; i < from.Length; i++)
            {
                from[i] = new YYY();
            }

            XXX x = (XXX)from[0];

            vec<XXX, YYY> coll = new vec<XXX, YYY>(from);

            // Not happy, this requires user have strong C# skill;
            //vec<XXX, YYY> coll = new vec<XXX, YYY>((v) => (XXX)v, from);

            foreach (var item in coll)
            {
                Debug.Print("Value is {0}", item.foo);
            }

        }
    }

}

The compiler error for T001 is: Cannot convert type 'T2' to 'T'

If you need to have your custom implicit/explicit conversion operators run, then casting to/from object (or performing an as cast) will skip them because the information is only known at compile-time when you are performing a runtime cast.

The only way I know of, through a general-use generic method such as you posted, is to leverage dynamic which will lookup at runtime to see if there are any conversion operators defined and call them:

return (T2)(dynamic)obj;

Quick example:

public class Class1
{

    public static implicit operator Class1(Class2 class2)
    {
        Console.WriteLine("implicit conversion from Class2 to Class1");
        return new Class1();
    }

    public static implicit operator Class2(Class1 class1)
    {
    Console.WriteLine("implicit conversion from Class1 to Class2");
        return new Class2();
    }
}

public class Class2
{

}

public static T2 Convert<T1, T2>(T1 obj)
{
    return (T2)(dynamic)obj;
}

var class1 = new Class1();
var class2 = Convert<Class1, Class2>(class1);
//outputs: implicit conversion from Class1 to Class2

Be aware though, that this using reflection and doing significant work at runtime, so thoroughly test and make sure performance is still acceptable.

EDIT: Since you do not have access to the dynamic language runtime, you can write your own conversion operator lookups with reflection:

public static T2 Convert<T1, T2>(T1 obj)
{
    var conversionOperator = typeof(T1).GetMethods(BindingFlags.Static | BindingFlags.Public)
    .Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
    .Where(m => m.ReturnType == typeof(T2))
    .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T1))
    .FirstOrDefault();

    if (conversionOperator != null)
        return (T2)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

You may need to tweak the code (perhaps to attempt traditional casting if no operators are found), and I'm not sure if I can guarantee that this will work every time. I don't know if there are corner cases or platform quirks to handle. Not to mention that this will be pretty slow with the reflection. You could introduce a quick caching mechanism where you do an O(1) lookup with a Dictionary or something where you can store each conversion operator as they're found for each type combination.

You can first cast it to object then T :

Add((T)(object)item)

But you should be cautious about run-time errors, and define T and T2 in a way that it does not cause an issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM