显式将泛型类型参数强制转换为任何接口

本文关键字:转换 任何 接口 泛型类型参数 | 更新日期: 2023-09-27 17:59:28

在Generics常见问题解答中:最佳实践说:

编译器将允许您将泛型类型参数显式转换为任何接口,但不转换为类:

interface ISomeInterface
{...}
class SomeClass
{...}
class MyClass<T> 
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;//Compiles
      SomeClass      obj2 = (SomeClass)t;     //Does not compile
   }
}

我认为限制对类和接口都是合理的,除非类/接口没有指定为约束类型。

那么,为什么会有这样的行为,为什么接口允许这样的行为呢?

显式将泛型类型参数强制转换为任何接口

我相信这是因为转换为SomeClass可以表示任意数量的内容,具体取决于可用的转换,而转换为ISomeInterface只能是引用转换或装箱转换。

选项:

  • 先投射到对象:

      SomeClass obj2 = (SomeClass) (object) t;
    
  • 改为使用as

      SomeClass obj2 = t as SomeClass;
    

显然,在第二种情况下,如果t而不是SomeClass,您还需要在之后执行无效性检查。

编辑:这方面的理由在C#4规范的第6.2.7节中给出:

上述规则不允许从无约束类型参数直接显式转换为非接口类型,这可能会令人惊讶。这个规则的原因是为了防止混淆,并使这种转换的语义清晰。例如,考虑以下声明:

class X<T>
{
    public static long F(T t) {
        return (long)t; // Error 
    }
} 

如果允许t到int的直接显式转换,则可以很容易地预期X<int>.F(7)将返回7L。然而,它不会,因为只有当类型在绑定时已知为数字时,才会考虑标准的数字转换。为了使语义清晰,必须编写上面的示例:

class X<T>
{
    public static long F(T t) {
        return (long)(object)t; // Ok, but will only work when T is long
    }
}

这段代码现在将进行编译,但执行X<int>.F(7)会在运行时引发异常,因为装箱的int不能直接转换为long。

在C#的继承原则中,接口可以被继承多次,但一个类只能继承一次。由于从接口继承具有复杂的层次结构,.net框架不需要确保泛型类型T在编译时是一个特定的接口。(编辑)相反,通过在编译时将类型约束声明为以下代码,可以确保类是一个特定的类。

class MyClass<T> where T : SomeClass
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;
      SomeClass      obj2 = (SomeClass)t;     
   }
}
相关文章: