当它不是静态时,如何使用现有方法而不是lambda?
这必须是重复但我还没有找到它。 我发现这个问题是相关的,因为它回答了为什么建议使用方法组而不是lambda。
但是,如果方法不在当前类中且方法不是static
我如何使用现有方法组而不是lambda?
假设我有一个我希望转换为字符串的整数列表,我可以使用List.ConvertAll
,但我需要将Converter
传递给它:
List ints = new List { 1 }; List strings = ints.ConvertAll(i => i.ToString());
这有效,但它使用lambda创建了一个不必要的匿名方法。 因此,如果Int32.ToString
是静态的并且将采用int
我可以写:
List strings = ints.ConvertAll(Int32.ToString);
但那当然不能编译。 那么我怎么能使用方法组呢?
如果我要创建这样的实例方法
string FooInt(int foo) { return foo.ToString(); }
我可以使用strings = ints.ConvertAll(FooInt);
,但这不是我想要的。 我不想创建一个新方法只是为了能够使用现有的方法。
框架中有一个静态方法,可用于将任何集成数据类型转换为字符串,即Convert.ToString
:
List ints = new List { 1 }; List strings = ints.ConvertAll (Convert.ToString);
由于Convert.ToString
的签名也是已知的,您甚至可以消除显式目标类型参数:
var strings = ints.ConvertAll(Convert.ToString);
这很有效。 但是,即使ReSharper告诉你不同的东西,我也更喜欢lambda表达式。 ReSharper有时会优化imho。 它可以防止开发人员考虑他们的代码,特别是在可读性方面。
更新
根据Tim的评论,我将尝试解释在这种特殊情况下lambda和静态方法组调用之间的区别。 因此,我首先了解了mscorlib
反汇编,以确定int-to-string转换是如何工作的。 Int32.ToString
方法在System
命名空间的Number
-class中调用外部方法:
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCritical] public string ToString(IFormatProvider provider) { return Number.FormatInt32(this, null, NumberFormatInfo.GetInstance(provider)); }
静态Convert.ToString
成员除了在参数上调用ToString
之外别无其他:
[__DynamicallyInvokable] public static string ToString(int value) { return value.ToString(CultureInfo.CurrentCulture); }
从技术上讲,如果您编写自己的静态成员或扩展名,就像在您的问题中所做的那样,没有区别。 那么这两条线之间有什么区别?
ints.ConvertAll(i => i.ToString()); ints.ConvertAll(Convert.ToString);
另外 – 从技术上讲 – 没有区别。 第一个示例创建一个匿名方法,它返回一个字符串并接受一个整数。 使用整数的实例,它调用它的成员ToString
。 第二个做同样的事情,除了该方法不是匿名的,而是框架的集成成员。
唯一的区别是第二行更短并且为编译器节省了一些操作。
但为什么不能直接调用非静态ToString
?
让我们看一下ConvertAll
方法:
public List ConvertAll (Converter converter) { if (converter == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); } List list = new List (this._size); for (int i = 0; i < this._size; i++) { list._items[i] = converter(this._items[i]); } list._size = this._size; return list; }
该列表迭代每个项目,以项目作为参数调用转换器,并将结果复制到最后返回的新列表中。
所以这里唯一的关系是你的converter
明确调用。 如果你可以将Int32.ToString
传递给方法,编译器必须决定在循环中调用this._items[i].ToString()
。 在这个特定的情况下,它可以工作,但这对编译器来说是“太多的智能”。 类型系统不支持此类代码转换。 相反,转换器是一个对象,描述了一个可以从被调用者的范围调用的方法。 这是一个现有的静态方法,比如Convert.ToString
,或者是一个匿名表达式,比如lambda。
导致基准测试结果差异的原因是什么?
这很难猜到。 我可以想象两个因素:
- 评估lambdas可能会导致运行时开销。
- 可以优化框架调用。
最后一点尤其意味着,JITer能够内联调用,从而获得更好的性能。 然而,这些只是我的假设。 如果有人能澄清这一点,我会很感激! 🙂
你亲自击中头部:
这有效,但它使用lambda创建了一个不必要的匿名方法。
你不能做你想要的,因为你没有合适的方法组可以使用,所以匿名方法是必要的 。 它适用于其他情况,因为隐式范围变量被传递给方法组创建的委托。 在您的情况下,您需要在范围变量上调用方法。 这是一个完全不同的场景。