为什么我们不能向泛型对象添加非泛型扩展方法?

Why are we unable to add non-generic extension methods to generic objects?

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

public static void Add(this IEnumerable<T> items, T item)
{
    ...
}

这引发了有关 T 未定义的预期编译器错误,因此我将签名更改为 Add<T> 以定义它。 (解释见this answer。)

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

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

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

我的问题是:

Why is there that limitation when it comes to extension methods? Is there something that has been explained for this limitation? Is it something that could be proposed to the language team and added in a future release?

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

I think I get what you're asking. Why is the compiler not smart enough to recognize, given the method signature, that T is already declared, and doesn't really need to be in the signature?

编辑

我应该在发布之前使用我的新方法 (Add<T>),因为我发现在使用该方法时,我不必一般地调用它,我可以只使用 .Add()。我想这与 this answer 一致。我仍然觉得它必须声明的方式很奇怪,也许这给整个情况增加了一个转折点。

反对 this question 重复的论点 提到创建 IEnumerable<T>.Add() 只是为了说明我发现这个 "peculiarity" 背后的推理,我的问题更通用,而不是特定于那个方法。

你可以,你只需要告诉编译器 T 是什么,因为它在扩展方法的上下文中不理解它。

public static void MyAdd<T>(this IList<T> items, T item)
{

}

当你使用它时,它会从项目中推断出类型,例如:

List<string> myList = new List<string>();
myList.MyAdd("this is added to the string");

在Intellisense中看起来有点奇怪,因为它在名称后面包含<>,但你不必使用它,它会自行推断类型。

正如其他评论所说,IEnumerable<T> 是一个接口,而不是 class,你不能 AddIEnumerable,所以你原来的 post 向不是真正集合的东西添加“[​​=13=]”扩展方法没有意义。但是如果你使用的是一个集合,比如 IList,那么它工作得很好。

I would still think that because of the this declaration on the initial parameter, that the in that parameter could be used to define the type for the method.

为什么?

假设你有这个方法:

public void Foo(int i1, T item)
{
}

编译器是否应该自动检测 T 并将其转换为:

public void Foo<T>(int i1, T item)
{
}

假设你有

public void Foo(int i1, T1 item1, T2 item2)
{
}

编译器应该把它翻译成什么?

public void Foo<T1, T2>(int i1, T1 item1, T2 item2)
{
}

或到

public void Foo<T2, T1>(int i1, T1 item1, T2 item2)
{
}

现在,使用扩展方法也是同样的问题。 T "this" 通用参数没有理由应该放在第一位...

public static void Foo<T2, T1>(this IEnumerable<T1> enu, T2 somethingelse)
{
}

这只是一个约定

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

public static void Add(this IEnumerable<X> items, X item)

...您认为这会奏效吗?如果是这样,请考虑:

public static void Add(this IEnumerable<Button> items, Button item)

您是否认为这意味着相同的事情,即实际上是采用 any 序列的通用方法,或者实际上只对 IEnumerable<Button> 有意义,其中 ButtonSystem.Windows.Forms.Button class 还是任何适当的 using 指令指示?

基本上,编译器需要查找T的意思。那可以是:

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

在您的情况下,您使用的是非泛型 class 和非泛型方法,因此它会退回到失败的普通类型查找。

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

如果您认为第一个示例(X)应该有效,那么您可能希望T被查看在 IEnumerable<> 类型的上下文中,这让事情变得更加奇怪,因为这意味着类型参数名称在 "client" 代码中变得很重要,在某种程度上它们在语言中的其他任何地方都没有。这也使其他事情变得棘手。例如:

static void Add(this IEnumerable<T> items, Dictionary<T, T> dict)

这里TIEnumerable<T>有效,但是Dictionary<,>TKeyTValue...所以编译器应该只说IEnumerable<T>赢了?它最终 比 "No, if you want a generic method, you have to declare a type parameter."

更令人困惑

目前,编译器在 static class 中编译 static 方法时必须做的所有事情,并观察到第一个参数前面有 this 是它必须在此方法上发出 ExtensionAttribute 。否则,它将以与任何其他(非扩展)static 方法完全相同的方式继续 parse/compile 此方法。

根据你的提议,它必须彻底改变它在这个一个特定地方的解析规则,并使语言less保持一致.我不会将其描述为限制,更像是语言功能如何协同工作的逻辑结果