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

Tim*_*ter 2 c# methods lambda type-conversion

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

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

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

List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(i => i.ToString());
Run Code Online (Sandbox Code Playgroud)

这有效,但它使用lambda创建了一个不必要的匿名方法.所以,如果Int32.ToString是静态的,int我会写:

List<string> strings = ints.ConvertAll<string>(Int32.ToString);
Run Code Online (Sandbox Code Playgroud)

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

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

string FooInt(int foo)
{
    return foo.ToString();
}
Run Code Online (Sandbox Code Playgroud)

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

Car*_*ten 7

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

List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(Convert.ToString);
Run Code Online (Sandbox Code Playgroud)

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

var strings = ints.ConvertAll(Convert.ToString);
Run Code Online (Sandbox Code Playgroud)

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

更新

根据Tim的评论,我将尝试解释在这种特殊情况下lambda和静态方法组调用之间的区别.因此,我首先看一下mscorlib反汇编来弄清楚,int-to-string转换是如何工作的.该Int32.ToString方法NumberSystem命名空间的-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));
}
Run Code Online (Sandbox Code Playgroud)

静态Convert.ToString成员除了调用ToString参数之外什么都不做:

[__DynamicallyInvokable]
public static string ToString(int value)
{
    return value.ToString(CultureInfo.CurrentCulture);
}
Run Code Online (Sandbox Code Playgroud)

从技术上讲,如果您编写自己的静态成员或扩展名,就像在您的问题中所做的那样,没有区别.那么这两条线之间有什么区别?

ints.ConvertAll<string>(i => i.ToString());
ints.ConvertAll(Convert.ToString);
Run Code Online (Sandbox Code Playgroud)

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

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

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

让我们来看看ConvertAll- 的方法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;
}
Run Code Online (Sandbox Code Playgroud)

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

所以这里唯一的关系就是你converter明确地调用了它.如果你可以传递Int32.ToString给方法,编译器必须决定this._items[i].ToString()在循环内调用.在这个特定的情况下,它可以工作,但这对于编译器来说是"太多的智能".类型系统不支持此类代码转换.相反,转换器是一个对象,描述了一个可以从被调用者的范围调用的方法.要么是现有的静态方法,Convert.ToString要么是匿名表达式,就像你的lambda一样.

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

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

  1. 评估lambdas可能会导致运行时开销.
  2. 可以优化框架调用.

最后一点尤其意味着,JITer能够内联调用,从而获得更好的性能.然而,这些只是我的假设.如果有人能澄清这一点,我会很感激!:)