如何在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'
如果需要运行自定义隐式/显式转换操作符,则向/从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)
但是您应该小心运行时错误,并以不会导致问题的方式定义T
和T2
。