显式强制转换与使用';作为';在泛型方法内部
本文关键字:作为 内部 泛型方法 转换 | 更新日期: 2023-09-27 17:52:12
我有一个简单的接口,有两个类实现它:
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
有效,而显式强制转换无效?
简单的答案是,因为T
不一定是正确的类型。编译器实际上是在试图帮助您,因为您正在做一些在运行时很容易失败的事情。
例如,考虑会发生什么
var result = DoSomething<Human>("ANI");
更长的答案是,你根本不应该选角。强制转换表示OOP设计存在问题,尤其是在使用泛型时是错误的:实际上,您失去了泛型的全部意义。泛型应该允许您创建一个"模板",抽象掉实际类型,让您担心算法本身,而不是具体类型。
在这种情况下,您可能根本不需要泛型。你的方法基本上是一种不太安全的方法:
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
甚至不安全。但这并不能解决与设计相关的根本问题。
使用您的代码,完全可以调用DoSomething<Animal>
,然后就有了(Animal)new Human()
这在生物学上是正确的,但你的模型不允许这样做
你真的需要仿制药吗?在这种情况下,也许您只是想返回IMovable
。
在尝试强制转换之前,"as"将首先进行"is"检查。因此,如果不能强制转换,它将不会尝试,然后返回null。