简体   繁体   中英

C# lambda, assign local variable vs return data


In C#, lambda can access local variable, and also return some data.

then, which is better in following situation?

int num;
Func<int> func = ()=>{return 10;}
num = func();

vs

int num;
Action action = ()=>{num = 10;}

I think, the performance is different. Which is better?



UPDATE(I dont know how to use StackOverflow)

My code here.

ErrorCode errorCode;
errorCode = DatabaseUtility.Read<ErrorCode>(
    conn,
    DatabaseUtility.CreateSelectQuery(....),
    reader =>
    {
        if(reader.Read())
            return ErrorCode.None;

        return ErrorCode.InvalidParam;
    });

But in this case, may be I can do like this.

ErrorCode errorCode;
DatabaseUtility.Read(
    conn,
    DatabaseUtility.CreateSelectQuery(....),
    reader =>
    {
        if(reader.Read())
            errorCode = ErrorCode.None;
        else
            errorCode = ErrorCode.InvalidParam;
    });

And, this is the method definition.

public static class DatabaseUtility
{
    public static Read<T>(
        MySqlConnection conn,
        string query,
        Func<MySqlDataReader, T> callback);
}

Gibbo is right: returning the value is clearer, so you should use that and not worry about performance, unless this code is in the 3% of code when microoptimizations make sense .

But returning the can also be more efficient, because it doesn't require heap allocation for the closure object and because it means num is going to be compiled as a local variable, not a field on the closure object (accessing local variables is cheaper than fields).

Also, if you're going to return the value, there is no reason to declare the variable so early, which will make your code slightly shorter:

Func<int> func = ()=>{return 10;}
int num = func();

The first one is better designed. It is reusable and can be called again and again on different variables (if required).

Performance I am not sure of, most likely negligible.

Actually there is a big semantical difference.

() => 10;
() => x = 10;

Both return 10. The difference is that in second case, the x variable is captured, bound to the lamba body, and can travel through contexts.

Imagine:

void SomeFunction()
{
    int variable;
    Execute((a) => variable = a);
}

void Execute(Action<int> statement)
{
    statement.Invoke(7);
}

Here SomeFunc has no idea what the value of variable will be when exiting the function context. On the other hand the Execute has no idea what will happen with the value passed into the function object through Invoke . This could be useful in some encapsulation contexts. Or possibly in situations where you hit C#'s generics limits (which is very easy by the way).

You can then think both ways when it comes to your code. eg "Do I want to handle the errors on my side, or do I provide the user of my interface with means of error handling?"

But then, I'd rather use abstract class to implement/enforce the aforementioned behaviour instead of lambdas.

Generally speaking, sending captured variables over contexts seems to hide dependencies and makes your code harder (in some cases maybe even impossible) to read and debug. I think it should only be used in scenarios where all other means of C# either yield very slow or very ugly code.

I want to raise a small issue with the code you posted:

public static class DatabaseUtility
{
    public static Read<T>(
        MySqlConnection conn,
        string query,
        Func<MySqlDataReader, T> callback);
}

the method has no return type signature! is it void? If it is void the code you posted won't work:

ErrorCode errorCode;
errorCode = DatabaseUtility.Read<ErrorCode>(
    conn,
    DatabaseUtility.CreateSelectQuery(....),
    reader =>
    {
        if(reader.Read())
            return ErrorCode.None;

        return ErrorCode.InvalidParam;
    });

if it is T already then there is no need to declare a Func to pass to the function Read.

I think in this very case return value of the function Read should be T, and given that you want to perform an action with the datatype you are retrieving (in case of some error or for some other case) it would be better off to pass an action instead:

public static class DatabaseUtility
{
    public static T Read<T>(
        MySqlConnection conn,
        string query,
        Action<MySqlDataReader> callback);
}

Premise that performance wise it should not be a problem unless you are targeting a real time application (which I think it's not your case). However that would be the neater solution IMHO.

hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM