我有一个简单的界面,有两个类实现它:
public interface IMovable { }
public class Human : IMovable { }
public class Animal : IMovable { }
以下通用方法导致编译时错误:Cannot convert type 'Human' to 'T'
public static T DoSomething<T>(string typeCode) where T : class, IMovable
{
if (typeCode == "HUM")
{
return (T)new Human(); // Explicit cast
}
else if (typeCode == "ANI")
{
return (T)new Animal(); // Explicit cast
}
else
{
return null;
}
}
但是当使用as
关键字时,一切都很好:
public static T DoSomething<T>(string typeCode) where T : class, IMovable
{
if (typeCode == "HUM")
{
return new Human() as T; // 'as'
}
else if (typeCode == "ANI")
{
return new Animal() as T; // 'as'
}
else
{
return null;
}
}
为什么as
有效,但明确的演员没有?
答案 0 :(得分:6)
简短回答是,因为T
不必是正确的类型。编译器真的试图帮助你,因为你正在做一些可能在运行时很容易失败的事情。
E.g。考虑一下会发生什么:
var result = DoSomething<Human>("ANI");
更长的答案是,你根本不应该投。转换表明您的OOP设计存在问题,使用泛型时尤其错误:实际上,您失去了泛型的全部要点。泛型应该允许你创建一个&#34;模板&#34;它抽象出实际类型,让你担心算法本身而不是具体类型。
在这种情况下,您可能根本不需要泛型。你的方法基本上不那么安全:
public static T DoSomething<T>() where T : new()
{
return new T();
}
或者这个:
public static IMovable DoSomething(string typeCode)
{
if (typeCode == "HUM")
return new Human();
if (typeCode == "ANI")
return new Animal();
return null;
}
为了使编译器静音,您还可以添加一个中间强制转换,它告诉编译器您需要额外的步骤来指示您真的想以这种方式强制转换:例如,使用
(T)(object)new Human()
或
(T)(IMovable)new Human()
都会通过编译,但从IMovable
到T
的转换并不比原始代码更安全,并且将object
转换为T
甚至更不安全。但这不是您的潜在问题的解决方案,这与设计有关。
答案 1 :(得分:5)
使用您的代码,完全可以拨打DoSomething<Animal>
,然后您(Animal)new Human()
。
这在生物学上是正确的,但你的模型不允许它。
你真的需要仿制药吗?也许你只想在这种情况下返回IMovable
。
答案 2 :(得分:1)
在封面下,&#39; as&#39;会做一个&#39;是&#39;在尝试演员之前先检查。因此,如果它不能投射它并且然后将返回null,它将不会尝试它。