输入类型的通用方法

我正在尝试编写通用方法来转换类型。 我想写一些像Cast.To(variable)而不是(Type) variable 。 我这个方法的错误版本:

 public class Cast { public static T To(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(a); 

正如您猜测的那样,此代码将因InvalidCastException而失败。

此代码是否失败,因为虚拟机不知道如何在运行时将类型object变量转换为类型B ? 但是exception消息说:“无法将类型A的对象转换为B类”。 所以CLR知道变量o实际类型,为什么它不能执行转换?

这里有一个主要问题:如何重写方法T To(object o)来解决这个问题?

如果您可以使用c#4.0,则可以:

 namespace CastTest { internal class Program { private static void Main(string[] args) { A a = new A(); B b = Cast.To(a); b.Test(); Console.Write("Done."); Console.ReadKey(); } public class Cast { public static T To(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(this object o) { return (T)(dynamic)o; } 

这里的关键是将o转换为dynamic将迫使.NET在运行时搜索显式运算符。

另外,为什么不把它作为一种扩展方法呢?

代替

  A a = new A(); B b = Cast.To(a); 

你可以做

  A a = new A(); B b = a.To(); 

将它作为扩展方法公开的另一个好处是,您可以获得用于显式转换的流畅界面(如果您喜欢这种事情)。 我总是讨厌.NET中显式转换所需的嵌套括号平衡量。

所以你可以这样做:

 a.To().DoSomething().To().DoSomethingElse() 

代替

 ((C)((B)a).DoSomething())).DoSomethingElse() 

对我来说,这看起来更清楚。

你可以通过Reflection找到正确的方法来做到这一点:

 public static T To (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()只是试图Cast.To()给定对象的引用解释为对T的引用。当然,它失败了。

如果编译器遇到(B) a并且知道a是类型A并且类型A具有编译时强制转换操作符以键入B – 它会发出此强制转换。 这不是你的情况。

实例a是转换为B的时刻的对象 。 不是类型,而是object 。 因此,不可能将objectB因为CLR无法知道, o包含显式运算符。
编辑:
是啊! 这是解决方案:

 public class Cast { public static T1 To(dynamic o) { return (T1) o; } } 

现在CLR确切地知道, o是类型A的实例,可以调用显式运算符。

如果没有“类型转换器”(对于所有已知类型的属性进行映射的手动过程,这根本不会发生),您永远不会让它工作。 你不能简单地将一个不相关的具体类转换为另一个。 它将破坏单一inheritance模型(这是现代OOP的定义原则之一 – 阅读’钻石问题’)

还注意到接口(多态) – 两个类也必须从相同的接口派生(沿着相同的行)

我实际上遇到过这个问题不止一次,并且当我可以将自己限制为实现IConvertible接口的类型时,它并不觉得OOP“脏”。 然后解决方案实际上变得非常干净!

 private T To(object o) where T : IConvertible { return (T)Convert.ChangeType(o, typeof(T)); } 

我在编写一个tokenizer时使用了这种变体,其中输入是一个字符串,但令牌可以解释为字符串,整数和双精度。

由于它使用Convert类,编译器实际上会有信息知道该怎么做。 这不仅仅是一个简单的演员。

如果你需要更通用的转换方法,我不得不怀疑这不是代码中的设计问题。 我认为扩大这些事情的范围的一个问题是,你试图覆盖的区域越多,外人就越难以知道该方法可以做多少。

我认为最重要的是,如果有人专门为作业编写了一个方法来避免像Add(x, y)的情况只是某些xy值,那么这个转换实际上是非常重要的。

我认为如果你自己尝试投射,期望是不同的,如T1 x = (T1) T2 y 。 然后我认为你更真实地依靠自己,因为你只是编造了一些演员,而不是称为“覆盖所有演员方法”。

在这种情况下,很明显它专门处理实现IConvertible对象,开发人员可以假设它可以很好地处理这些对象。

也许是一个面向对象 – 哲学的重要答案,并不是每个人都会同意,但我认为这些“概念问题”往往最终出现在编程哲学中。

也许不是你要做什么准确,但这将有效:

 public class Cast { public static targetType To(object o) { return (targetType)((sourceType) o); } } 

但是,这样的方法对我来说似乎毫无用处……