无法将委托转换为泛型类型 T。

8

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

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

 T CreateDelegate<T>() {… }

很不幸,泛型无法将 T 约束为派生自 MulticastDelegate 的类型,编译时会出现以下错误:

Constraint cannot be special class 'System.MulticastDelegate'

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

Cannot convert type 'System.Delegate' to 'T'

然而,如果我先将其转换为 object,然后再转换为 T,就可以正常工作:
T h = (T) ((object) Delegate.CreateDelegate(typeof(T), target, m));

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

这段代码是否有效:T h = Delegate.CreateDelegate(typeof(T), target, m) as T; - Ehsan Sajjad
是的,那样做可以。而且,这甚至更没有意义。在我看来,as_cast_两个操作符的行为应该保持一致。 - Miguel Gamboa
这种方式比将对象强制转换为T类型再从对象中转换更安全,如果转换失败,它会返回null。 - Ehsan Sajjad
有时候抛出“异常”是期望的行为,而不是返回“null”。 - Miguel Gamboa
2
这个主题有Eric Lippert的帖子。如果你对语言设计,特别是C#感兴趣,那么一定要阅读Eric的所有博客。 - usr
显示剩余2条评论
1个回答

14
C#语言对从类型X到类型Y的强制转换进行静态检查,即编译器可以(在一定程度上)保证兼容性并拒绝在编译时明显的错误。不受限制的泛型类型T和System.Delegate没有直接相关性。但是,当它们被转换为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的博客,例如这篇有关转换和类型参数的文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接