How to use existing method instead of lambda when it's not static?

旧城冷巷雨未停 提交于 2019-12-10 10:41:43

问题


This must be a duplicate but i haven't found it. I've found this question which is related since it answers why it's recommended to use a method group instead of a lambda.

But how do i use an existing method group instead of a lambda if the method is not in the current class and the method is not static?

Say i have a list of ints which i want to convert to strings, i can use List.ConvertAll, but i need to pass a Converter<int, string> to it:

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

This works, but it creates an unnecessary anonymous method with the lambda. So if Int32.ToString would be static and would take an int i could write:

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

But that doesn't compile - of course. So how can i use a method group anyway?

If i'd create an instance method like this

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

i could use strings = ints.ConvertAll<string>(FooInt);, but that is not what i want. I don't want to create a new method just to be able to use an existing.


回答1:


There is an static method in the framework, that can be used to convert any integrated data type into a string, namely Convert.ToString:

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

Since the signature of Convert.ToString is also known, you can even eliminate the explicit target type parameter:

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

This works. However, I'd also prefer the lambda-expression, even if ReSharper tells you something different. ReSharper sometimes optimizes too much imho. It prevents developers from thinking about their code, especially in the aspect of readability.

Update

Based on Tim's comment, I will try to explain the difference between lambda and static method group calls in this particular case. Therefor, I first took a look into the mscorlib disassembly to figure out, how int-to-string conversion exactly works. The Int32.ToString method calls an external method within the Number-class of the System namespace:

[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
    return Number.FormatInt32(this, null, NumberFormatInfo.GetInstance(provider));
}

The static Convert.ToString member does nothing else than calling ToString on the parameter:

[__DynamicallyInvokable]
public static string ToString(int value)
{
    return value.ToString(CultureInfo.CurrentCulture);
}

Technically there would be no difference, if you'd write your own static member or extension, like you did in your question. So what's the difference between those two lines?

ints.ConvertAll<string>(i => i.ToString());
ints.ConvertAll(Convert.ToString);

Also - technically - there is no difference. The first example create's an anonymous method, that returns a string and accepts an integer. Using the integer's instance, it calls it's member ToString. The second one does the same, with the exception that the method is not anonymous, but an integrated member of the framework.

The only difference is that the second line is shorter and saves the compiler a few operations.

But why can't you call the non-static ToString directly?

Let's take a look into the ConvertAll-method of 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;
}

The list iteraterates over each item, calls the converter with the item as an argument and copys the result into a new list which it returns in the end.

So the only relation here is your converter that get's called explicitly. If you could pass Int32.ToString to the method, the compiler would have to decide to call this._items[i].ToString() within the loop. In this specific case it would work, but that's "too much intelligence" for the compiler. The type system does not support such code conversions. Instead the converter is an object, describing a method that can be called from the scope of the callee. Either this is an existing static method, like Convert.ToString, or an anonymous expression, like your lambda.

What causes the differences in your benchmark results?

That's hard to guess. I can imagine two factors:

  1. Evaluating lambdas may result in runtime-overhead.
  2. Framework calls may be optimized.

The last point especially means, that the JITer is able to inline the call which results in a better performance. However, those are just assumptions of mine. If anyone could clarify this, I'd appreciate it! :)




回答2:


You hit the nail on the head yourself:

This works, but it creates an unnecessary anonymous method with the lambda.

You can't do what you're asking for because there is no appropriate method group that you can use so the anonymous method is necessary. It works in that other case because the implicit range variable is passed to the delegate created by the method group. In your case, you need the method to be called on the range variable. It's a completely different scenario.



来源:https://stackoverflow.com/questions/23908548/how-to-use-existing-method-instead-of-lambda-when-its-not-static

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!