【问题标题】:Cannot cast delegate to a generic type T无法将委托转换为泛型类型 T
【发布时间】:2016-02-10 17:09:46
【问题描述】:

为什么我们不能将 delegate 的实例转换为泛型类型T

考虑一个实用方法CreateDelegate,它创建一个T的实例,它是一个委托,即从MulticastDelegate派生的类型。

 T CreateDelegate<T>() {… }

不幸的是,泛型不允许将 T 约束为派生自 MulticastDelegate 的类型,会出现以下编译错误:

约束不能是特殊类'System.MulticastDelegate'

不过,此实用程序方法正在检查 T 是否与 MulticastDelegate 兼容,并通过 Delegate::CreateDelegate 的反射创建委托。但是,如果我们尝试将Delegate::CreateDelegate 的结果强制转换为T,我们会得到如下编译错误:

无法将类型“System.Delegate”转换为“T”

但是,如果我先将其转换为object,然后再转换为T,它将正常工作:

T h = (T) ((object) Delegate.CreateDelegate(typeof(T), target, m));

为什么我们不能直接将委托强制转换为 T?

【问题讨论】:

  • 这行得通吗:T h = Delegate.CreateDelegate(typeof(T), target, m) as T; ?
  • 是的,这行得通。而且,它更没有意义。恕我直言,两个运算符(ascast)的行为应该是一致的。
  • 这种方式比将对象转换为 T 更安全,如果转换失败,它会给你 null
  • 有时抛出Exception 是期望的行为,而不是返回null
  • 有 Eric Lippert 关于这个主题的帖子。如果您对语言设计特别是 C# 感兴趣,请阅读所有 Erics 博客。

标签: c# .net generics


【解决方案1】:

C# 语言强制静态检查从类型 X 到类型 Y 的强制转换是否有效——即它是否有意义,因为编译器可以(在一定程度上)保证兼容性并拒绝错误在编译时很清楚。不受约束的泛型类型TSystem.Delegate 没有直接的共同点。但是,当转换为object 时,编译器知道每个 类型本质上都是object,因此它允许转换。这并不意味着运行时类型检查在特定情况下不会失败。

as 操作符稍微宽松一点,因为它不会导致无效转换的异常。编译器在应用静态检查方面也不那么严格。在您的特定情况下,这很有帮助,因为您可以省略object 的中间转换并使用as T。但是,其中一项要求是 as 仅适用于类类型,因此您必须应用 where T : class 约束。

所以方法看起来像这样(简化):

public T CreateDelegate<T>(…) where T : class
{
    return Delegate.CreateDelegate(typeof(T), …) as T;
}

正如@usr 所建议的,推荐阅读 Eric Lippert 的博客,例如this article on casts and type parameters.

【讨论】:

    猜你喜欢
    • 2018-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-30
    相关资源
    最近更新 更多