C#中的方法组是什么?

409

我经常在如下情况下遇到这样的错误:"无法将'method group'转换为'string'":

var list = new List<string>();
// ... snip
list.Add(someObject.ToString);
当然,最后一行中有一个错别字,因为我忘记在ToString之后加上调用括号。正确的形式应该是:
var list = new List<string>();
// ... snip
list.Add(someObject.ToString()); // <- notice the parentheses

然而,我开始想知道什么是方法组。Google并没有提供太多帮助,MSDN 也不是很有用。


69
C# 3.0 规范的第 7.1 节定义了“方法组”。 - Eric Lippert
4
你在调用方法时没有加括号,这就是我现在读到的错误信息。 - niico
2
如果您有一个适当的委托类型列表,例如 var list = new List<Func<string>>();,那么方法组就可以使用,并且 list.Add(someObject.ToString); 就可以工作了。 - Jeppe Stig Nielsen
5个回答

385

方法组是指一组方法(可能只有一个)的名称 - 即理论上ToString方法可以有多个重载(以及任何扩展方法):ToString()ToString(string format)等等,因此ToString本身就是一个“方法组”。

通过重载解析,通常可以将方法组转换为(有类型的)委托 - 但不能转换为字符串等;这没有意义。

一旦添加括号,重载解析再次启动,您已经明确地标识了一个方法调用。


7
方法组的典型用途是什么?因为(我理解的是)它们具有相同的名称,参数数量和/或类型将不同。因此,您无法使用该组调用多个方法。 - Andrei Rînea
37
这是编译器术语,表示“我知道方法名,但不知道签名”,在运行时并不存在。据我所知,仅限于委托构造期间可单独使用方法组(没有括号等)。 - Marc Gravell
21
ECMA 334v4 §14.1:方法组可以在调用表达式(§14.5.5)中使用,在委托创建表达式(§14.5.10.3)中使用,或者隐式转换为兼容的委托类型。在任何其他上下文中,将表达式分类为方法组会导致编译时错误。 - Marc Gravell
1
协变性和逆变性对于方法组的支持允许将方法签名与委托类型匹配。这使您不仅可以分配具有匹配签名的方法,还可以分配返回更派生类型(协变性)或接受具有比委托类型指定的类型更少派生类型的参数(逆变性)的方法。有关更多信息,请参见“委托中的变化(C#)”和“使用委托中的变化(C#)”。 - SKARVA Bodavula
每当我遇到这个错误时,都是因为我忘记了方法调用中的括号,因为我以为它是一个属性。类似“你忘记了()”这样的错误信息实际上99%的情况下都很有用哈哈。 - niico

191

此外,如果您正在使用LINQ,您似乎可以执行以下操作:myList.Select(methodGroup)

例如,我有:

private string DoSomethingToMyString(string input)
{
    // blah
}

不要像这样明确地声明要使用的变量:

public List<string> GetStringStuff()
{
    return something.getStringsFromSomewhere.Select(str => DoSomethingToMyString(str));
}

我可以省略变量的名称:

public List<string> GetStringStuff()
{
    return something.getStringsFromSomewhere.Select(DoSomethingToMyString);
}

127
一条ReSharper的建议让我去寻找方法组。这就是ReSharper对我的一个linq表达式所做的事情。 - a_hardin
55
@ a_hardin:ReSharper非常棒!由于Resharper,我学到了很多有关LINQ的知识。 - Jason Down
3
“更加明确”的版本只是创建了一个本质上无用的包装方法。匿名的包装方法和DoSomethingToMyString都接受一个字符串并返回一个字符串。 "str => ..." 字符串只是编译器希望优化掉的无用代码。 - Grault
7
编译器不进行任何优化。使用ILDASM,你可以看到显式版本创建了一个匿名方法(即lambda实现),并将其传递给Select()。而隐式版本则直接将DoSomethingToMyString传递给了Select()。 - Rolf
18
让我想起了“return flag == true ? true : false”(扶额)。 - ErikE
显示剩余3条评论

38
你可以将一个方法组转换成委托。
委托签名从组中选择一个方法。
以下示例选择带有字符串参数的 ToString() 重载:
Func<string,string> fn = 123.ToString;
Console.WriteLine(fn("00000000"));

这个例子选择了不带参数的ToString()重载:
Func<string> fn = 123.ToString;
Console.WriteLine(fn);

22

你在 MSDN 搜索结果中的第一个条目说:

方法组标识要调用的方法集合或要从中选择特定方法以调用的一组重载方法

我的理解是,当你只写 someInteger.ToString 时,它可能是指:

Int32.ToString(IFormatProvider) 

或者它可以指:

Int32.ToString()

因此它被称为方法组。


20

ToString函数有许多重载——方法组将是该函数的所有不同重载的组合。


2
是的,关键在于方法组是一个编译时构造。编译器根据消息组使用的上下文选择一个方法重载。例如:您不能编写“var x = Foo;”(其中Foo是一个方法组),即使只有一个重载,编译器也会拒绝此操作。 - Rolf

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