[英]Generic method to type casting
我正在尝试编写通用方法来转换类型。 我想写类似Cast.To<Type>(variable)
而不是(Type) variable
的东西。 我对这种方法的错误版本:
public class Cast
{
public static T To<T>(object o)
{
return (T) o;
}
}
这是一个简单的测试:
public class A
{
public static explicit operator B(A a)
{
return new B();
}
}
public class B
{
}
A a = new A();
B b = Cast.To<B>(a);
如您所料,此代码将因InvalidCastException
而失败。
此代码是否失败是因为虚拟机不知道如何在运行时将object
类型的变量转换为B
类型? 但异常消息显示:“无法将 A 型的 object 转换为 B 型”。 所以 CLR 知道变量o
的真实类型,为什么它不能执行强制转换?
这是主要问题:我应该如何重写方法T To<T>(object o)
来解决这个问题?
关于运营商解决方案的所有内容都是正确的......但这是我对您的主要问题的回答:
public static T To<T>(this object o)
{
return (T)(dynamic)o;
}
这里的关键是将 o 转换为 dynamic 将强制 .NET 在运行时搜索显式运算符。
另外,为什么不让它成为一种扩展方法呢?
代替
A a = new A();
B b = Cast.To<B>(a);
你可以做
A a = new A();
B b = a.To<B>();
将其公开为扩展方法的另一个好处是,您可以获得一个用于显式转换的流畅接口(如果您喜欢那种东西)。 我一直讨厌在 .NET 中显式转换所需的嵌套括号平衡量。
所以你可以这样做:
a.To<B>().DoSomething().To<C>().DoSomethingElse()
代替
((C)((B)a).DoSomething())).DoSomethingElse()
对我来说,这看起来更清晰。
如果您可以使用 c# 4.0 这可以:
namespace CastTest
{
internal class Program
{
private static void Main(string[] args)
{
A a = new A();
B b = Cast.To<B>(a);
b.Test();
Console.Write("Done.");
Console.ReadKey();
}
public class Cast
{
public static T To<T>(dynamic o)
{
return (T)o;
}
}
public class A
{
public static explicit operator B(A a)
{
return new B();
}
}
public class B
{
public void Test()
{
Console.WriteLine("It worked!");
}
}
}
}
你可以通过反射找到正确的方法来做到这一点:
public static T To<T> (object obj)
{
Type sourceType = obj.GetType ();
MethodInfo op = sourceType.GetMethods ()
.Where (m => m.ReturnType == typeof (T))
.Where (m => m.Name == "op_Implicit" || m.Name == "op_Explicit")
.FirstOrDefault();
return (op != null)
? (T) op.Invoke (null, new [] { obj })
: (T) Convert.ChangeType (obj, typeof (T));
}
在 .NET 4.0 中,您可以按照其他答案中的建议使用dynamic
关键字。
您的Cast.To<T>()
只是试图将对给定 object 的引用解释为对 T 的引用。这当然失败了。
如果编译器遇到(B) a
并且知道a
是类型A
并且类型A
具有类型B
的编译时强制转换运算符 - 它会发出此强制转换。 这不是你的情况。
我实际上不止一次遇到过这个问题,当我可以将自己限制为实现IConvertible
接口的类型时,它并不觉得 OOP “脏”。 然后解决方案实际上变得非常干净!
private T To<T>(object o) where T : IConvertible
{
return (T)Convert.ChangeType(o, typeof(T));
}
例如,当我编写一个标记器时,我使用了它的一个变体,其中输入是一个字符串,但是标记可以被解释为字符串、整数和双精度数。
由于它使用的是Convert
class,因此编译器实际上会有信息知道该做什么。 这不仅仅是一个简单的演员阵容。
如果您需要一种更通用的转换方式,我不得不怀疑这是否不是代码中的设计问题。 我认为为这些事情扩大 scope 的一个问题是,您尝试覆盖的区域越多,外人就越难知道该方法可以做什么。
我认为最重要的是,当有人专门为这项工作编写了一种方法以避免像Add(x, y)
这样的情况时,只有x
和y
的某些值才能进行强制转换。
我认为如果您自己尝试铸造,预期会有所不同,如T1 x = (T1) T2 y
。 然后我认为更明显的是你真的靠自己,因为你只是编造了一些演员而不是所谓的“覆盖所有演员方法”。
在这种情况下,很明显它专门处理实现IConvertible
的对象,开发人员可以假设它可以很好地处理这些对象中的任何一个。
也许一个 object 面向哲学的沉重答案甚至不是每个人都会同意,但我认为这类“概念性问题”通常最终会出现在编程哲学中。
实例a
是一个object到转换为B
的时刻。 不是A
类型,而是object
。 因此,不可能将object
为B
,因为 CLR 无法知道o
包含显式运算符。
编辑:
是的:这是解决方案:
public class Cast
{
public static T1 To<T1>(dynamic o)
{
return (T1) o;
}
}
现在 CLR 确切地知道, o
是类型A
的实例并且可以调用显式运算符。
如果没有“类型转换器”(一个手动映射所有已知类型的属性的过程,这根本不会发生),你永远不会让它工作。 您根本不能只将一个不相关的混凝土 class 浇铸到另一个混凝土上。 它会破坏单个 inheritance model (这是现代 OOP 的定义原则之一 - 阅读“钻石问题”)
还注意到接口(多态性) - 两个类也必须从相同的接口派生(沿着相同的路线)
也许不是你要做什么,但这会起作用:
public class Cast
{
public static targetType To<soureType, targetType>(object o)
{
return (targetType)((sourceType) o);
}
}
但是好吧,这样的方法对我来说似乎没用......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.