我正在尝试编写通用方法来转换类型。我想写一些像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的对象转换为类型B”。所以CLR知道变量o
的实际类型,为什么它不能执行转换?
这是一个主要问题:我应该如何重写方法T To<T>(object o)
来解决这个问题?
答案 0 :(得分:15)
所有关于操作员解决方案的说法都是正确的......但这是我对你主要问题的回答:
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()
对我来说,这看起来更清楚。
答案 1 :(得分:12)
如果您可以使用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!");
}
}
}
}
答案 2 :(得分:4)
你可以通过反思找到正确的方法来做到这一点:
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
关键字。
答案 3 :(得分:2)
你的Cast.To<T>()
只是试图将对给定对象的引用解释为对T.的引用。当然这是失败的。
如果编译器遇到(B) a
并且知道a
类型为A
且类型A
具有编译时强制转换运算符以键入B
- 它发出这个演员。这不是你的情况。
答案 4 :(得分:1)
实例a
是投射到B
时的对象。不是A
类型,而是object
。因此,由于CLR无法知道object
包含显式运算符,因此无法将B
转换为o
。
修改强>
是啊!这是解决方案:
public class Cast
{
public static T1 To<T1>(dynamic o)
{
return (T1) o;
}
}
现在CLR完全知道,o
是A
类型的实例,可以调用显式运算符。
答案 5 :(得分:1)
如果没有“类型转换器”(对于所有已知类型的属性进行映射的手动过程,这根本不会发生),您将永远无法工作。您根本无法将一个不相关的具体类转换为另一个。它将破坏单一继承模型(这是现代OOP的定义原则之一 - 阅读'钻石问题')
还注意到接口(多态) - 两个类也必须从同一个接口派生(沿着相同的行)
答案 6 :(得分:1)
我实际上遇到过这个问题不止一次,当我可以将自己限制为实现IConvertible
接口的类型时,它感觉不是OOP“脏”。然后解决方案实际上变得非常干净!
private T To<T>(object o) where T : IConvertible
{
return (T)Convert.ChangeType(o, typeof(T));
}
当我编写一个tokenizer时,我使用了这种变体,其中输入是一个字符串,但令牌可以解释为字符串,整数和双精度。
由于它使用Convert
类,编译器实际上会有信息知道该怎么做。这不仅仅是一个简单的演员。
如果你需要一种更通用的方法,我不得不想知道这不是代码中的设计问题。我认为扩大这些问题的范围的一个问题是,你试图覆盖的区域越多,外人就越难以知道该方法可以做多少。
我认为最重要的是,如果有人专门为工作编写了一种方法,以避免出现Add(x, y)
这样的情况,只有x
和{{1}的某些值,那么这种情况真的很有效。 }。
我认为如果你自己尝试投射,期望是不同的,如y
。然后我认为你更真实地依靠自己,因为你只是编造了一些演员,而不是称为“覆盖所有演员方法”。
在这种情况下,很明显它专门处理实现T1 x = (T1) T2 y
的对象,开发人员可以假设它可以很好地处理任何这些对象。
也许一个面向对象 - 哲学的重要答案,并不是每个人都会同意,但我认为这些“概念问题”往往最终出现在编程哲学中。
答案 7 :(得分:0)
也许不是你要做什么,但这样可行:
public class Cast
{
public static targetType To<soureType, targetType>(object o)
{
return (targetType)((sourceType) o);
}
}
但是,这种方法对我来说似乎毫无用处......