where does 'p' come from in
NotifyPropertyChanged(p => QuantitySaved);
The lambda is being passed to a method called NotifyPropertyChanged
. There is one overload of that method. It has formal parameter type Expression<Func<ViewModelBase, T>>
. That is, the formal parameter expects to get a lambda that takes a ViewModelBase and returns a T, for some T.
The p
is the parameter that the lambda takes.
The compiler is able to infer that the author of the code neglected to spell out the type of the lambda parameter explicitly. The author could also have written:
NotifyPropertyChanged((ViewModelBase p) => QuantitySaved);
had they wanted to be explicit about it.
how does the compiler know to fill in a type value of ViewModelBase from thin air?
The compiler examines all possible overloads of NotifyPropertyChanged
that could possibly take a lambda in that argument position. It infers the formal parameter type of the lambda from the delegate types that are in the formal parameter types of the NotifyPropertyChanged
methods. An example might help. Suppose we have:
void M(Func<int, double> f) {}
void M(Func<string, int> f) {}
and a call
M(x=>x.Length);
The compiler must infer the type of the lambda parameter x. What are the possibilities? There are two overloads of M. Both take a delegate in the formal parameter of M corresponding to the first argument passed in the call. In the first one, the function is from int to double, so x could be of type int. In the second one, the formal parameter of M is a function from string to int, so x could be string.
The compiler must now determine which one is correct. In order for the first one to be correct, the body of the lambda must return a double. But if x is int, there is no property Length on x that returns a double. So x cannot be int. Can x be string? Yes. There is a property Length on x that returns an int if x is string.
Therefore the compiler deduces that x is string.
These deductions can get extraoridinarily complicated. A slightly more complicated example:
void M<A, B, C>(A a1, Func<List<A>, B> a2, Func<B, C> a3) {}
...
M(123, x=>x.Count.ToString(), y=>y.Length);
Type inference must infer the types A, B, C and therefore the types of x and y. The compiler first infers that A must be int since a1 is 123. It then infers that x must be List<int>
from that fact. It then infers that B must be string, and therefore y is string, and therefore C is the type of y.Length
, which is int.
It gets a lot more complicated from there, believe me.
If this subject interests you, I have written a number of articles and filmed some videos on the subject of various kinds of type inference performed by the compiler. See
http://blogs.msdn.com/b/ericlippert/archive/tags/type+inference/
for all the details.
For fun I changed the code from 'p' to 'this', since SampleViewModel inherits from ViewModelBase, but I was met with a series of compiler errors, the first one of which statedInvalid expression term '=>' This confused me a bit since I thought that would work.
The only acceptable left-hand-side of a lambda operator is a lambda parameter list; "this" is never a legal lambda parameter list. The compiler is expecting "this" to be followed by ".SomeMethod()" or some such thing; the compiler assumes that "this" will never be followed by "=>". When you violate that assumption, bad things happen.