简体   繁体   English

如何在C#泛型类中调用类型转换运算符?

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

It seems we cannot call type conversion operator easily in C# generic class. 似乎我们不能在C#泛型类中轻松调用类型转换运算符。 Here is the code. 这是代码。 Why? 为什么?

T006 finally archive our target. T006最终存档了我们的目标。

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' T001的编译器错误是: 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. 如果您需要运行自定义的隐式/显式转换运算符,则对object强制转换(或执行as 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: 通过您所发布的通用通用方法,我知道的唯一方法是利用dynamic ,该方法将在运行时进行查找以查看是否定义了任何转换运算符并调用它们:

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. 您可以引入一种快速缓存机制,在该机制中,您可以使用Dictionary进行O(1)查找,或者可以存储针对每种类型组合找到的每个转换运算符。

You can first cast it to object then T : 您可以先将其转换为object然后转换为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. 但是您应该谨慎对待运行时错误,并以不会引起问题的方式定义TT2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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