如何在c#泛型类中调用类型转换操作符

本文关键字:调用 类型转换 操作符 泛型类 | 更新日期: 2023-09-27 18:12:21

似乎不能在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'

如何在c#泛型类中调用类型转换操作符

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

我所知道的唯一方法,通过您发布的通用通用方法,是利用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