为什么我们无法将非泛型扩展方法添加到通用对象?

kri*_*gar -2 c# generics extension-methods

我终于厌倦了IEnumerable没有Add方法,并决定通过扩展方法添加我自己的方法.我最初的尝试是这样的:

public static void Add(this IEnumerable<T> items, T item)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这引发了关于T未定义的预期编译器错误,因此我将签名更改Add<T>为定义它.(有关解释,请参阅此答案.)

但是,这让我思考.如果我们自己创建一个泛型类(比如IEnumerable<T>),我们可以像我最初尝试的那样添加方法,因为它T是在类中定义的.

我知道扩展方法不是作为类的一部分创建的.编译器中没有"魔术"将它们添加到原始类中.

我仍然认为,由于this初始参数的声明<T>,该参数可以用于定义方法的类型.

我的问题是:

为什么扩展方法存在这种限制?是否有针对此限制的解释?是否可以向语言团队提出并在未来的版本中添加?

更具体地说,Jonsey更有说服力地重申了我的观点:

我想我得到了你所要求的.为什么编译器不够智能,在给定方法签名的情况下,已经声明了T,并且实际上不需要在签名中?

编辑

我应该Add<T>在发布之前使用我的新方法(),因为我发现在使用该方法时,我不必一般地调用它,我可以使用.Add().我想这与这个答案一致.我仍然觉得它必须被宣布的方式很奇怪,也许这会给整个情况带来麻烦.

反对这个问题重复的争论 提到创造IEnumerable<T>.Add()仅仅是为了说明我发现这种"特殊性"背后的推理,我的问题更通用,不是特定于那种方法.

Jon*_*eet 9

那么,你需要问问自己T这里有什么特别之处.假设我们要写:

public static void Add(this IEnumerable<X> items, X item)
Run Code Online (Sandbox Code Playgroud)

......你希望它能起作用吗?如果是这样,请考虑:

public static void Add(this IEnumerable<Button> items, Button item)
Run Code Online (Sandbox Code Playgroud)

你会想到,意味着同样的事情,即有效地泛型方法服用任何序列,或者实际上只能使意义的IEnumerable<Button>地方ButtonSystem.Windows.Forms.Button类或任何适当的using指令指示?

基本上,编译器需要查找是什么T意思.那可以是:

  • 声明类中的类型参数
  • 声明方法中的类型参数
  • 普通类型查找

在您的情况下,您处于非泛型类和非泛型方法中,因此它会回退到正常类型查找失败.

基本上你希望它是一个类型参数,所以它必须是一个泛型方法(或类,但泛型类中不允许使用扩展方法).

如果你认为第一个例子(与X)应该工作,那么想必你期待T的背景下要查找IEnumerable<>的类型,这使得事情更奇怪,因为这意味着该类型的参数名称从成为重要的"客户端"代码的方式不是语言中的任何其他地方.它还使其他事情变得棘手.例如:

static void Add(this IEnumerable<T> items, Dictionary<T, T> dict)
Run Code Online (Sandbox Code Playgroud)

T是有效的IEnumerable<T>,但Dictionary<,>TKey,TValue...所以编译器应该说IEnumerable<T>胜利?它最终被比只是说更混乱"不行,如果你想有一个通用的方法,你必须声明的类型参数."