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

C# 为什么我们不能向泛型对象添加非泛型扩展方法?,c#,generics,extension-methods,C#,Generics,Extension Methods,我终于厌倦了IEnumerable没有Add方法,决定通过扩展方法添加我自己的方法。我最初的尝试大致如下: public static void Add(this IEnumerable<T> items, T item) { ... } publicstaticvoidadd(此IEnumerable项,T项) { ... } 这引发了关于未定义T的预期编译器错误,因此我将签名更改为Add来定义它。(有关说明,请参见。) 然而,这让我思考。如果我们自己创建一个泛型类(比

我终于厌倦了
IEnumerable
没有
Add
方法,决定通过扩展方法添加我自己的方法。我最初的尝试大致如下:

public static void Add(this IEnumerable<T> items, T item)
{
    ...
}
publicstaticvoidadd(此IEnumerable项,T项)
{
...
}
这引发了关于未定义
T
的预期编译器错误,因此我将签名更改为
Add
来定义它。(有关说明,请参见。)

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

我知道扩展方法不是作为类的一部分创建的。编译器中不存在将它们添加到原始类的“魔法”

我仍然认为,由于对初始参数的
声明,该参数中的
可以用于定义方法的类型

我的问题是:

当涉及到扩展方法时,为什么会有这种限制?对于这个限制有什么解释吗?它是否可以向语言团队提出并在未来的版本中添加

更具体地说,琼西更雄辩地重申了我的观点:


我想我明白你的要求了。为什么编译器不够聪明,在给定方法签名的情况下,不能识别T已经声明,并且不需要在签名中

编辑

在发布之前,我应该使用我的新方法(
Add
),因为我发现在使用该方法时,我不必泛泛地调用它,我只需使用
.Add()
。我想这是应该的。我仍然觉得它的表述方式很奇怪,也许这会给整个情况增加一个转折点

反对重复的论点
提到创建
IEnumerable.Add()
仅仅是为了说明我发现这个“特性”的原因,我的问题更一般,而不是特定于那个方法。

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

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

}
publicstaticvoidmyadd(此IList项,T项)
{
}
当您使用它时,它将根据项目推断类型,例如:

List<string> myList = new List<string>();
myList.MyAdd("this is added to the string");
static void Add(this IEnumerable<T> items, Dictionary<T, T> dict)
List myList=new List();
myList.MyAdd(“这被添加到字符串中”);
它在Intellisense中看起来有点奇怪,因为它在名称后面包含了,但您不必使用它,它会自己推断类型

正如其他评论所说,
IEnumerable
是一个接口,而不是一个类,并且您不能将
添加到
IEnumerable
,因此您最初的帖子没有意义将“
Add
”扩展方法添加到并非真正的集合中。但是如果您使用的是一个集合,比如
IList
,那么它就可以正常工作

我仍然认为,由于对初始参数的声明,该参数中的可以用于定义方法的类型

为什么?

假设您有以下方法:

public void Foo(int i1, T item)
{
}
编译器是否应自动检测
T
,并将其转换为:

public void Foo<T>(int i1, T item)
{
}
编译器是否应该将其翻译为

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

这只是一个惯例。

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

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

此处
T
IEnumerable
有效,但
Dictionary
具有
TKey
TValue
。。。那么编译器应该说
IEnumerable
获胜吗?它最终比仅仅说“不,如果你想要一个泛型方法,你必须声明一个类型参数”更令人困惑,当编译器在
static
类中编译
static
方法时,它所要做的就是观察到第一个参数前面有
this
,它必须在这个方法上发出。否则,它将继续以与任何其他(非扩展)
static
方法完全相同的方式解析/编译此方法


根据您的建议,它必须在这一特定位置彻底更改其解析规则,这会降低语言的一致性。我不会把它描述为一种限制,更多的是语言功能如何协同工作的逻辑结果。

我不理解这个问题-你可以向泛型类型添加扩展方法,你只需要像对待
IEnumerable
那样抽象相关的类型参数。为什么不使用IList或ICollection呢?IEnumerable是枚举集合,而不是添加到集合中……我只是好奇,当类型绑定到它在
this
参数中操作的对象时,为什么需要以通用方式声明方法。也许周末我会抽出时间深入研究一下Roslyn的源代码,看看原因,尽管我不知道在那里能找到多少文档。为什么编译器不够聪明,在给定方法签名的情况下,不能识别
T
已经声明,并且实际上不需要在签名中?我完全不同意,并且认为它是一个扩展方法的事实确实改变了事情。如果
this
参数是泛型类型,那么通过它是泛型的事实,应该能够定义
T1
是什么。在您的示例中,我希望该方法是
Foo
,因为如果您编写自己的
IEnumerable
接口,则需要声明该方法。@krillgar:您可以说您希望它更改某些内容,但除非您能够指出语言中的位置
public static void Foo<T2, T1>(this IEnumerable<T1> enu, T2 somethingelse)
{
}
public static void Add(this IEnumerable<X> items, X item)
public static void Add(this IEnumerable<Button> items, Button item)
static void Add(this IEnumerable<T> items, Dictionary<T, T> dict)