Pergunta

I need to understand how a developer can make use of Action delegates and lambdas. I will give an example:

For example, using MOQ, you write something like:

var repoMock = new Mock<IMyInterface>();
repoMock.Setup(r => r.GetData()).Returns(new string[] {"one", "two", "three"});

My point is, how's the code inside "Setup" and "Returns" is making use of the input (lambda expression)?

Foi útil?

Solução

If you look at the type signatures for the Moq methods, you will notice that Setup and Returns are actually doing to very different things. Returns takes a delegate (a Func<TResult> or a Func<T, TResult>), but Setup actually takes an Expression, which is a much more complex data type. More details about the difference between Func<T> (a delegate) and Expression<Func<T>> (an expression) can be found in this related answer. The rest of my answer will try to explain how Moq, specifically, uses those two different types for two different reasons. Hopefully that will at least get you started in how to use them in your own code.

Delegates, whether they be named, anonymous, or lambda expressions, are .NET's way of permitting functions to be used as a "first class" objects; that's just a fancy way of saying that delegates can be used in all the same ways that the primitive types: you can declare local delegates, pass delegates as parameters, and return delegates from methods. You "call" a delegate the same way you call any other function: you use the () syntax.

In your case, you used the overload of Returns that doesn't actually take a delegate, it simply takes an value that will be returned whenever the associated mocked method is called. However, there are overloads of Returns that do take delegates, so you could have equivalently written this:

.Returns(() => new string[] {"one", "two", "three"}};

or

.Returns(x => x.produceStringArray("one", "two", "three"));

In either case, the Returns method will be given a delegate, of the appropriate type, as a parameter. Internally, all Returns really does is to save the delegate as part of the mocked object instance. Later, when the mocked method gets called, the delegate is executed to return the correct value. While the real Moq internals are more complex that this, the basic idea is something like the following:

private Func<T> returnMe;

public void Returns<T>(Func<T> myDelegate)
{
  this.returnMe = myDelegate;
}

public T Execute()
{
  return this.returnMe();
}

As you can see, once you have a delegate, you can just call it like any other function; when the compiler sees a lambda expression somewhere that it expects a delegate, it compiles the lambda expression into an anonymous delegate and that's what gets passed into your method.

The Setup method, on the other hand, is far more complex. The lambda expression there is not being passed as a Func<T>, but as an Expression<Func<T>>. What this means is, the compiler is not compiling the lambda expression into a delegate, but rather, into an expression tree. The objects in an expression tree are special types of object that represent the various things that a method can do. Compilers produce expression trees out of your source code all the time, but usually those expression trees are immediately used to produce machine code. When the C# compiler compiles an Expression, however, it actually leaves the expression tree in it's "half-compiled" state. You can finish compiling an expression, and run the resulting code if you want, but that's not the only way they are used. Your program can also examine the expression tree and see all the details about what the code "would do" if it were compiled and run, and use that information however it wants.

When you call a Moq method like this:

moq.Setup(x => x.MyMethod(1, 2, 3)).Returns(true);

the Setup method can use the expression tree to figure out the name of the method, and the types and values of the parameters, that you wrote on the right-hand side of your lambda. In this case, Moq never executes your lambda expression, merely examines it. It creates an internal object that is associated with the specific method call you just set up, and records the various things you associate with that method (e.g. the Return calls.) Later on, when you use your mocked object in a unit test, Moq will intercept every method call you make on your object, and scan its list of set-up methods looking for one that matches the call, and produce whatever mocked-up behavior you've specified about that call.

Outras dicas

It is like the Setup method calls (or stores a reference to, for future use) a method with parameter "r" which returns r.GetData() value.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top