繁体   English   中英

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

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

似乎我们不能在C#泛型类中轻松调用类型转换运算符。 这是代码。 为什么?

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);
            }

        }
    }

}

T001的编译器错误是: Cannot convert type 'T2' to 'T'

如果您需要运行自定义的隐式/显式转换运算符,则对object强制转换(或执行as cast)将跳过它们,因为仅在执行运行时强制转换时才在编译时知道该信息。

通过您所发布的通用通用方法,我知道的唯一方法是利用dynamic ,该方法将在运行时进行查找以查看是否定义了任何转换运算符并调用它们:

return (T2)(dynamic)obj;

快速示例:

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

但是请注意,此操作使用了反射并在运行时做了很多工作,因此请进行全面测试并确保性能仍然可以接受。

编辑:由于您无权访问动态语言运行库,因此可以使用反射来编写自己的转换运算符查找:

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");
}

您可能需要调整代码(如果找不到操作符,则可能尝试进行传统的转换),而且我不确定是否可以保证每次都能正常工作。 我不知道是否需要处理极端情况或平台怪癖。 更不用说反射会很慢了。 您可以引入一种快速缓存机制,在该机制中,您可以使用Dictionary进行O(1)查找,或者可以存储针对每种类型组合找到的每个转换运算符。

您可以先将其转换为object然后转换为T

Add((T)(object)item)

但是您应该谨慎对待运行时错误,并以不会引起问题的方式定义TT2

暂无
暂无

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

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