4

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.

TylerH
  • 20,799
  • 66
  • 75
  • 101
LTrain
  • 59
  • 3
  • Which logging package are you using? – Dan Wilson Jun 20 '18 at 14:11
  • 1
    Here is no difference. In another context it may differ – Backs Jun 20 '18 at 14:11
  • 1
    That basically depends on what `Log` is but I would suspect that in this case it makes no difference. Using the `()=>` syntax you are just creating a new anonymous function that is returning a string (the same string as below). – PhilMasteG Jun 20 '18 at 14:11
  • 1
    Too much flexibility, a `Func` strikes me as overkill. Look at *all* the overloads of the Info() method, surely it has one that takes a plain string. – Hans Passant Jun 20 '18 at 14:11
  • Which package does `Log.Info(...)` come from? You need to check the documentation for the `Log.Info(Func func)` overload. – Tamir Daniely Jun 20 '18 at 14:12
  • I could be wrong but I honestly don't think that does anything, where did you see that first example? because the only reason you'd want to pass a delegate is for a callback, but that doesn't make sense. I can't see any documentation on the Log.Info class that shows the use of a delegate. – JoeTomks Jun 20 '18 at 14:13
  • 1
    One reason for the first approach is for if it's *expensive* to generate the required string. You're effectively giving the logging system control over if the string generation even occurs - e.g. if info-level messages are excluded from actual logging, etc. – Kirk Larkin Jun 20 '18 at 14:16
  • First one means: *hey log method, use this delegate*. The delegate does nothing but return a string. The 2nd one means: *hey log method, use this string.* – CodingYoshi Jun 20 '18 at 14:20

2 Answers2

19

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.

xanatos
  • 109,618
  • 12
  • 197
  • 280
5

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.

Ian
  • 33,605
  • 26
  • 118
  • 198
  • @bornfromanegg Thanks. I just thought the same and edited the answer same time as you were posting. I've been doing too much JavaScript with no overloading! – Ian Jun 20 '18 at 14:15