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

时间:2022-09-11 10:36:06

In Generics FAQ: Best Practices says :

在泛型常见问题中:最佳实践说:

The compiler will let you explicitly cast generic type parameters to any interface, but not to a class:

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

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

I see limitation reasonable for both, classes and interfaces, unless the class/interface is not specified as constraint type.

除非未将类/接口指定为约束类型,否则我认为类和接口的限制都是合理的。

So why such behavior, why it is allowed for interfaces ?

那么为什么这样的行为,为什么它被允许接口呢?

2 个解决方案

#1


43  

I believe this is because the cast to SomeClass can mean any number of things depending on what conversions are available, whereas the cast to ISomeInterface can only be a reference conversion or a boxing conversion.

我相信这是因为对SomeClass的强制转换可能意味着任何数量的事情取决于可用的转换,而对ISomeInterface的强制转换只能是引用转换或装箱转换。

Options:

选项:

  • Cast to object first:

    首先投射到对象:

    SomeClass obj2 = (SomeClass) (object) t;
    
  • Use as instead:

    用作代替:

    SomeClass obj2 = t as SomeClass;
    

Obviously in the second case you would also need to perform a nullity check afterwards in case t is not a SomeClass.

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

EDIT: The reasoning for this is given in section 6.2.7 of the C# 4 specification:

编辑:对此的推理在C#4规范的第6.2.7节中给出:

The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear. For example, consider the following declaration:

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

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

If the direct explicit conversion of t to int were permitted, one might easily expect that X<int>.F(7) would return 7L. However, it would not, because the standard numeric conversions are only considered when the types are known to be numeric at binding-time. In order to make the semantics clear, the above example must instead be written:

如果允许将t直接显式转换为int,则可能很容易预期X .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
    }
}

This code will now compile but executing X<int>.F(7) would then throw an exception at run-time, since a boxed int cannot be converted directly to a long.

此代码现在将编译但执行X .F(7)将在运行时抛出异常,因为boxed int无法直接转换为long。

#2


2  

In the inheritance principle of C#, interfaces could be inherited multiple times, but a class just once. As the inheritance from interfaces has complex hierarchy, the .net framework does not need to ensure the generic type T a specific interface at the compile time.(EDIT) On the contrary, a class could be ensured a specific class with declaring a type constraint at the compile as the following code.

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

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

#1


43  

I believe this is because the cast to SomeClass can mean any number of things depending on what conversions are available, whereas the cast to ISomeInterface can only be a reference conversion or a boxing conversion.

我相信这是因为对SomeClass的强制转换可能意味着任何数量的事情取决于可用的转换,而对ISomeInterface的强制转换只能是引用转换或装箱转换。

Options:

选项:

  • Cast to object first:

    首先投射到对象:

    SomeClass obj2 = (SomeClass) (object) t;
    
  • Use as instead:

    用作代替:

    SomeClass obj2 = t as SomeClass;
    

Obviously in the second case you would also need to perform a nullity check afterwards in case t is not a SomeClass.

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

EDIT: The reasoning for this is given in section 6.2.7 of the C# 4 specification:

编辑:对此的推理在C#4规范的第6.2.7节中给出:

The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear. For example, consider the following declaration:

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

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

If the direct explicit conversion of t to int were permitted, one might easily expect that X<int>.F(7) would return 7L. However, it would not, because the standard numeric conversions are only considered when the types are known to be numeric at binding-time. In order to make the semantics clear, the above example must instead be written:

如果允许将t直接显式转换为int,则可能很容易预期X .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
    }
}

This code will now compile but executing X<int>.F(7) would then throw an exception at run-time, since a boxed int cannot be converted directly to a long.

此代码现在将编译但执行X .F(7)将在运行时抛出异常,因为boxed int无法直接转换为long。

#2


2  

In the inheritance principle of C#, interfaces could be inherited multiple times, but a class just once. As the inheritance from interfaces has complex hierarchy, the .net framework does not need to ensure the generic type T a specific interface at the compile time.(EDIT) On the contrary, a class could be ensured a specific class with declaring a type constraint at the compile as the following code.

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

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