LTrain
LTrain

Reputation: 59

Purpose of the () => vs just getting a property directly

So I am learning more C# and came across this syntax:

Log.Info(() => $"Some Text {SomeVariableorProperty}");

How does this differ from the following?

Log.Info($"Some Text {SomeVariableorProperty}");

I know that the () => is basically a delegate but not sure what its purpose is here.

Upvotes: 4

Views: 192

Answers (2)

xanatos
xanatos

Reputation: 111870

The scenario is:

Log.Info(() => $"Some Text {SomeSlowMethod()}");

public static string SomeSlowMethod()
{
    Thread.Sleep(5000);
    return "Foo";
}

Now... What happens if the logging of Info is disabled? Is the SomeSlowMethod called? The answer is no! Because the calling of the delegate () => $"Some Text {SomeSlowMethod()} is done by Log.Info() if it needs to do it. Compare it with:

Log.Info($"Some Text {SomeSlowMethod()}");

Now SomeSlowMethod() is always called, and there is the possibility that Log.Info will ignore its value.

If you think that SomeSlowMethod()s in real case scenarios don't exist, remember that even string composition is "slow" compared to other operations. Simply doing useless string.Format ($"Some Text {SomeVariableorProperty} is a string.Format) is a waste of time. And one of the laws of a good logging library is that if it isn't active it shouldn't slow your application.

To make an interesting comparison, Debug.Assert():

[Conditional("DEBUG")]
public static void Assert(bool condition, string message);

Here message is a string, but you see the [Conditional(...)]? if DEBUG isn't defined at compile time, the C# compiler can remove the whole Debug.Assert() method call, and even remove all the methods that are called inside the Debug.Assert(), so modifying possible side effects:

Debug.Assert(false, Throw());

public static string Throw()
{
    throw new Exception();
}

If DEBUG isn't defined (so you are executing a RELEASE build), this will be converted to:

; // nothing

so no throw (see for example this). But note that this must be resolved at compilation time, while logging libraries are configured at runtime.

Upvotes: 19

Ian
Ian

Reputation: 34489

It means that the Log.Info method is expecting a function with the signature Func<String>, essentially a parameterless function that will return a string.

Passing $"Some Text {SomeVariableorProperty}" directly will fail when building, as this is a String and not a function that can be executed. That is - unless the method itself has overloads that accept just a String.

If you're in complete control over the code, then completely agree that it's a little odd, I can't see a strong reason for wanting to use a function over a String.

The only good use case for this as @KirkLarkin suggests is if the generation of that Log message needs to be done in a lazy manner. You'd probably only need this in an edge case scenario where the creating the actual message to log is an expensive operation. That way if you're call to Log.Info() decides it doesn't need to log it (e.g. it's too verbose based on a setting) you can bypass the expensive message generation. As I say though - it'd be rare that you'd come across a situation like this and probably indicates that too much is being logged.

Upvotes: 5

Related Questions