C#: Func<> instead of methods? [duplicate]

2020-06-02 13:37发布

问题:

This is a curiosity questions for you all in the know:

Is there any harm/downside to using a Func instead of a method? Simple example:

private static Func<int, int, DBContext, List<T>> Foo =
    (i1, i2, dbc) =>
        (i1 != 0) ? dbc.Bar(i2) : new List<T> { /*some default values ...*/ };

Vs

private static List<T> Foo(int i1, int i2, DBContext dbc)
{
    return i1 != 0 ? dbc.Bar(i2) : new List<T> { /*some default values ...*/ };
}

回答1:

I see severale downsides:

  • performance impact (delegate vs method) - small but it's there
  • no parameternames (hurts readability on calls)
  • the definition itself is less readable
  • no overload possible (thanks to xanatos)

as you gain nothing I would only to so in a local and small context and prefer the static method



回答2:

Performance is not as much of an argument as it would seem originally, the cost of invoking a delegate and a method that is dispatched through the v_table (virtual instance methods) are comparable. In this case, for a static Method it is likely there is a small performance gain for not using the delegate.

The "trick" you discovered here is a fairly common technique to achieve functional decomposition without resorting to object-oriented programming techniques (e.g. the Strategy Pattern). You replace a method call with a call to a delegate. Another nice aspect of this trick is that the syntax for invocation is the same.

However, I would be extremely careful applying this technique. What I do usually use it for is to avoid Interfaces with a single method, that do not have more than one or two parameters (else I would consider using a Parameter Object or using an interface).

C# is a multi-paradigm language, and using each paradigm has its place. Functional paradigms (such as using Funcs instead of compile-time known methods) have their place, as do object oriented paradigms. In your case, there is no benefit over using a Func<>, you should use a traditional method for clarity instead.



回答3:

In the first case (with Func), you're not writing a method, but a variable Foo that you initialize with a specific delegate. You have one level of indirection that you can use to change what Foo points to. To simplify your example:

public static class Test {

    public static Func<int, int, int> Foo =
        (i1, i2) =>
            i1 + i2;

    public static int Foo2(int i1, int i2)
    {
        return i1 + i2;
    }
}

let's test:

int a = Test.Foo(2,3);
int b = Test.Foo2(2,3);

Console.WriteLine("{0}, {1}",a,b); // 5, 5, all seems good, they're both sums

    //... but wait... now you can do this:
Test.Foo = (x,y) => x * y; //reassigning Foo
int c = Test.Foo(2,3);
Console.WriteLine("{0}",c); // 6 

so, if you are usign this level of indirection, the Func approach makes sense. But if you're not using it, you have introduced one level of indirection too many, with impact on performance, on how well your code communicates its intent and on the correctness of your program (since your method can now be switched for another that does something different at runtime)



回答4:

ideally you wouldnt wanna create a Func if there is just going to be one definition of it always.. you are unnecessarily increasing the complexity and readability of the code and thus affecting the maintainability..

if you have strong reasons to use it like you create Func dynamically based on certain conditions then using Func<> is justified..

Also take a look at Action. here is a blog that I read to explain when to use what: http://simpleprogrammer.com/2010/09/24/explaining-what-action-and-func-are/



回答5:

From the "learning book" perspective it should be the same, but

  • small performance impact, because lambdas/delegates are little-bit slower than methods
  • parameters have generic names (e.g. arg1, arg2)
  • Intellisense is generally not able to provide documentation tooltips
  • Methods defined as lambda fields cannot introduce new generic parameters
  • readability impact


回答6:

Apart from the readablity downgrade I don't think there's any other harm. Not sure about how the code compiles, but on the first version you are just defining the function using a delegate. I suppose you could also do the following:

private static List<T> Foo(int i1, int i2, DBContext dbc)
{
    i1 != 0 ? return dbc.Bar(i2) : return new List<T> { /*some default values ...*/ };
}

private static Func<int, int, DBContext, List<T>> Foo2 = Foo;