wingerse
wingerse

Reputation: 3796

Method group behaving differently than lambda?

I am using Moq to mock some interface. Here it is:

var titleGenerator = new Mock<ITitleGenerator>();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(Guid.NewGuid().ToString);

Console.WriteLine(titleGenerator.Object.GenerateTitle());
Console.WriteLine(titleGenerator.Object.GenerateTitle());

It prints the same value twice. But if I replace the second line with this:

titleGenerator.Setup(t => t.GenerateTitle()).Returns(() => Guid.NewGuid().ToString());

it returns unique values on each call.

I always thought method groups are just a shortcut to lambda expression. Is there any difference? I tried searching the documentations for any explanation. Can someone enlighten me?

It looks like the method group evaluates the expression once and somehow caches it? Or does it have something to do with Moq?

Upvotes: 0

Views: 236

Answers (3)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61952

For an equivalent example that might be easier to understand, the code:

var id = 1;
Func<string> f = id.ToString;
id = 2;
Console.WriteLine(f()); // 1

will write "1", whereas:

var id = 1;
Func<string> f = () => id.ToString();
id = 2;
Console.WriteLine(f()); // 2

will write "2".

In the first case, the delegate (Func<> instance) f is created with the value 1 as Target and the method info for string int.ToString() as Method. The later reassignment to id does not affect f.

In the second case, things will be more indirect. The compiler will generate a new method that corresponds to the => arrow. The local variable id is captured or closed over (is in the closure of the lambda). That means, behind the scenes, id is really promoted to a field somewhere (compiler's choice). When your method mentions id, it really accesses that field. And the compiler-generated method corresponding to the => arrow also reads that field. Now the Func<> is created with its Method property being this compiler-generated method. Because of all this, the result will be "2" here. That is the closure semantics of anonymous functions in C#.

Your original Moq example is just the same. The Returns overload in question takes an argument Func<TResult> valueFunction where TResult is string in your use. That valueFunction is what I called f in my simpler example.

Upvotes: 0

D Stanley
D Stanley

Reputation: 152556

The difference is in the inputs. In the first case the "method group" is actually a delegate for Guid.ToString. But since it requires an instance as "input", the instance is part of the delegate expression, and so the same "input" is used each time.

It would be equivalent to:

var titleGenerator = new Mock<ITitleGenerator>();
Guid g = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(g.ToString);

In the second case, the delegate has no input. The Guid instance is calculated within the delegate, so a new Guid is used each time.

Upvotes: 0

Patrick Quirk
Patrick Quirk

Reputation: 23747

In your first example, you're passing the ToString function of a single Guid, which then gets called on every invocation. It's equivalent to this:

Guid guid = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(guid.ToString)

In your second example, you're passing a function that first creates a new Guid and then invokes ToString() on it.

Upvotes: 9

Related Questions