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

发布于 2024-11-16 02:52:31 字数 487 浏览 0 评论 0原文

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

编译器将允许您显式强制转换任何接口的通用类型参数,但不是类:

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

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

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

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 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨轻弹 2024-11-23 02:52:31

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

选项:

  • 首先转换为对象:

     SomeClass obj2 = (SomeClass) (object) t;
    
  • 使用 as 代替:

     SomeClass obj2 = t as SomeClass;
    

显然,在第二种情况下,您还需要随后执行无效检查,以防 t 不是 SomeClass

编辑:C# 4 规范的第 6.2.7 节给出了这样做的原因:

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

类 X;
{
    公共静态长 F(T t) {
        返回(长)t; // 错误 
    }
} 

如果允许将 t 直接显式转换为 int,人们可能很容易认为 X.F(7) 将返回 7L。但是,事实并非如此,因为只有在绑定时已知类型为数字时才会考虑标准数字转换。为了使语义清晰,上面的例子必须改写:

类 X;
{
    公共静态长 F(T t) {
        返回(长)(对象)t; // 好的,但只有当 T 很长时才有效
    }
}

此代码现在可以编译,但执行 X.F(7) 会在运行时引发异常,因为装箱 int 无法直接转换为 long。

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.

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.

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

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:

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.

风蛊 2024-11-23 02:52:31

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

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

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.

class MyClass<T> where T : SomeClass
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;
      SomeClass      obj2 = (SomeClass)t;     
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文