显式强制转换与使用';作为';在泛型方法内部

本文关键字:作为 内部 泛型方法 转换 | 更新日期: 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() 

将通过编译,尽管从IMovableT的转换并不比原始代码更安全,并且将object转换为T甚至不安全。但这并不能解决与设计相关的根本问题。

使用您的代码,完全可以调用DoSomething<Animal>,然后就有了(Animal)new Human()
这在生物学上是正确的,但你的模型不允许这样做

你真的需要仿制药吗?在这种情况下,也许您只是想返回IMovable

在尝试强制转换之前,"as"将首先进行"is"检查。因此,如果不能强制转换,它将不会尝试,然后返回null。