当它不是静态时,如何使用现有方法而不是lambda?

时间:2014-05-28 10:02:39

标签: c# methods lambda type-conversion

这必须是重复但我还没找到。我发现this question是相关的,因为它回答了为什么建议使用方法组而不是lambda。

但是,如果方法不在当前类中并且方法不是static,我如何使用现有方法组而不是lambda?

假设我有一个我希望转换为字符串的整数列表,我可以使用List.ConvertAll,但我需要将Converter<int, string>传递给它:

List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(i => i.ToString());

这样可行,但它会使用lambda创建一个不必要的匿名方法。因此,如果Int32.ToString是静态的并且需要int我可以写:

List<string> strings = ints.ConvertAll<string>(Int32.ToString);

但那当然不能编译。那么我怎么能使用方法组呢?

如果我要创建像这样的实例方法

string FooInt(int foo)
{
    return foo.ToString();
}

我可以使用strings = ints.ConvertAll<string>(FooInt);,但这不是我想要的。我不想创建一个新方法只是为了能够使用现有的。

2 个答案:

答案 0 :(得分:7)

框架中有一个静态方法,可用于将任何集成数据类型转换为字符串,即Convert.ToString

List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(Convert.ToString);

由于Convert.ToString的签名也是已知的,您甚至可以消除显式目标类型参数:

var strings = ints.ConvertAll(Convert.ToString);

这很有效。但是,即使ReSharper告诉你不同的东西,我也更喜欢lambda表达式。 ReSharper有时会优化imho。它可以防止开发人员考虑他们的代码,特别是在可读性方面。

更新

根据Tim的评论,我将尝试解释在这种特殊情况下lambda和静态方法组调用之间的区别。因此,我首先看一下mscorlib反汇编来弄清楚,int-to-string转换是如何工作的。 Int32.ToString方法在Number命名空间的System - 类中调用外部方法:

[__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<string>(i => i.ToString());
ints.ConvertAll(Convert.ToString);

另外 - 技术上 - 没有区别。第一个示例创建一个匿名方法,它返回一个字符串并接受一个整数。使用整数的实例,它调用它的成员ToString。第二个做同样的事情,除了该方法不是匿名的,而是框架的集成成员。

唯一的区别是第二行更短并且为编译器节省了一些操作。

但为什么不能直接调用非静态ToString

我们来看看ConvertAll的{​​{1}}方法:

List

列表迭代每个项目,以项目作为参数调用转换器,并将结果复制到最后返回的新列表中。

所以这里唯一的关系就是明确调用你的public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) { if (converter == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); } List<TOutput> list = new List<TOutput>(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(),或匿名表达式,如lambda。

导致基准测试结果差异的原因是什么?

这很难猜到。我可以想象两个因素:

  1. 评估lambdas可能会导致运行时开销。
  2. 可以优化框架调用。
  3. 最后一点尤其意味着,JITer能够内联调用,从而获得更好的性能。然而,这些只是我的假设。如果有人能澄清这一点,我会很感激! :)

答案 1 :(得分:3)

你自己打了一针:

  

这样可行,但它会创建一个不必要的匿名方法   拉姆达。

您无法执行您要求的操作,因为您无法使用适当的方法组,因此匿名方法必要。它适用于其他情况,因为隐式范围变量传递给方法组创建的委托。在您的情况下,您需要调用范围变量的方法。这是一个完全不同的场景。