我终于厌倦了IEnumerable
没有Add
方法,并决定通过扩展方法添加我自己的方法。我最初的尝试是这样的:
public static void Add(this IEnumerable<T> items, T item)
{
...
}
这引发了关于T
未定义的预期编译器错误,因此我将签名更改为Add<T>
以定义它。 (有关说明,请参阅this answer。)
然而,这让我想到了。如果我们自己创建一个泛型类(比如IEnumerable<T>
),我们可以像我最初尝试的那样添加方法,因为在类中定义了T
。
我知道扩展方法不是作为类的一部分创建的。没有&#34;魔法&#34;在编译器中发生,将它们添加到原始类中。
我仍然认为,由于初始参数的this
声明,该参数中的<T>
可用于定义方法的类型。
我的问题是:
为什么扩展方法存在这种限制?是否有针对此限制的解释?是否可以向语言团队提出并在未来的版本中添加?
更具体地说,Jonsey更有说服力地重申了我的观点:
我想我得到了你所要求的。为什么编译器不够智能,在给定方法签名的情况下,已经声明了T,并且确实不需要在签名中?
修改
我应该在发布之前使用我的新方法(Add<T>
),因为我发现在使用该方法时,我不必一般地调用它,我可以使用.Add()
。我想这与this answer一致。我仍然觉得它必须被宣布的方式很奇怪,也许这会给整个情况带来麻烦。
反对this question 重复的论点
提及创建IEnumerable<T>.Add()
仅仅是为了说明我找到这个&#34;特殊性&#34;背后的原因,而我的问题更通用,而不是特定于那个方法。
答案 0 :(得分:9)
嗯,你需要问问自己这里T
有什么特别之处。假设我们要写:
public static void Add(this IEnumerable<X> items, X item)
......你希望它能起作用吗?如果是这样,请考虑:
public static void Add(this IEnumerable<Button> items, Button item)
你是否希望这意味着同样的事情,即有效地采用任何序列的通用方法,或者实际上只对IEnumerable<Button>
有意义,其中Button
是System.Windows.Forms.Button
类或任何适当的using
指令表示什么?
基本上,编译器需要查找T
的含义。那可以是:
在您的情况下,您处于非泛型类和非泛型方法中,因此它会回退到正常类型查找失败。
基本上你希望这是一个类型参数,所以它必须是泛型方法(或类,但泛型类中不允许使用扩展方法)。
如果你不认为第一个例子(X
)应该有用,那么大概是你期望T
在上下文中被查找IEnumerable<>
类型,这使得事情变得更奇怪,因为它意味着类型参数名称从“客户端”代码变得重要,因为它们不是语言中的任何其他地方。它还使其他事情变得棘手。例如:
static void Add(this IEnumerable<T> items, Dictionary<T, T> dict)
此处T
对IEnumerable<T>
有效,但Dictionary<,>
有TKey
,TValue
...所以编译器应该只说IEnumerable<T>
胜利?它最终只是很多比仅仅说“不,如果你想要一个通用方法,你必须声明一个类型参数。”
答案 1 :(得分:3)
你可以,你只需要告诉编译器什么是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中看起来有点奇怪,因为它包含&lt;&gt;在名称之后,但您不必使用它,它将自己推断出类型。
正如其他评论所说,IEnumerable<T>
是一个界面,而不是一个类,你不能Add
到一个IEnumerable
,所以你的原始帖子没有意义将“Add
”扩展方法添加到不是真正集合的内容中。但是如果您使用的是IList
之类的集合,那么它的工作正常。
答案 2 :(得分:1)
我仍然认为,由于初始参数的这个声明,该参数可用于定义方法的类型。
为什么?
假设你有这种方法:
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
“这个”通用论证应该是第一个......
public static void Foo<T2, T1>(this IEnumerable<T1> enu, T2 somethingelse)
{
}
这只是惯例。
答案 3 :(得分:1)
目前,编译器在static
类内部编译static
方法并观察到第一个参数前面有this
时,所有编译器必须执行的操作是它必须在此方法上发出ExtensionAttribute
。否则,它继续以与任何其他(非扩展)static
方法完全相同的方式解析/编译此方法。
根据您的提议,它必须从根本上改变它在一个特定位置的解析规则,并使语言 less 保持一致。我不会将其描述为限制,而是将语言功能如何协同工作的逻辑结果。