Hello Clifford,
maybe it helps if I first elaborate a bit on lambda expressions and then explain why it is not possible what you aim to do with
properties.
Delegate: what they are for
A delegate is the means of C# to hold a handle to a method without executing the method. Once you have that handle, you can execue the method the handle points to at any time.
This is
deferred execution. This is the primary purpose of delegates.
Ways to define delegates
The traditional way
The traditional way is to define a delegate type and use it wherever you need that deferred execution capability, e.g.
public delegate void PrintStringDelegate(string arg);
public static void ToConsole(string s)
{
Console.WriteLine("{0}", s);
}
PrintStringDelegate print;
print = new PrintStringDelegate(ToConsole);
print = ToConsole;
print = delegate(string s) { Console.WriteLine("{0}", s); };
print("Hello deferred world!");
Become a bit fancy: Action, Action<...>, and Func<...>
Instead of defining your own delegate type, you may use the already predefined generic equivalents. In the above case we have an action that takes one string argument (a function would return a value, but the example above doesn't, so, the Func is not the right generic delegate).
E.g. the same as above, but with Action:
Action<string> print;
print = new Action<string>(ToConsole);
print = ToConsole;
print = delegate(string s) { Console.WriteLine("{0}", s); };
print("Hello deferred world!");
BTW: there are further such generic delegates defined for convenience, e.g.
Predicate<in T>
, which is identical to
Func<in T, bool>
, and again identical to
public delegate bool MyPredicateDelegate<in T>(T arg);
.
Even more fancy: lambda expressions
As we've seen above, we can "simplify" my taking Action and Func in place of our own delegate type definition.
We can also define the method the delegate points inplace, i.e. at the point where we define the delegate variable. This was already possible with the anonymous methods, e.g.
print = delegate(string s) { Console.WriteLine("{0}", s); };
Lambda expression allow to simplify that further, e.g.,
print = (s) => { Console.WriteLine("{0}", s); };
Lambda expressions are anonymous methods, identical in effect to the anonymous methods defined via the
delegate
keyword as shown above.
Even lambda expressions can be of any complexity, they are most handy for small inline expressions, therefore, there are some abbreviatons possible, e.g.
print = s => { Console.WriteLine("{0}", s); };
print = s => Console.WriteLine("{0}", s);
A nicety of lambda expresions is that you can use variables from its immediate environment (unlike methods), e.g.
string tag = "";
print = s => Console.WriteLine("{0}: {1}", tag, s);
tag= "Error";
print("some");
tag= "Warning";
print("more text");
The output is:
Error: some text
Warning: more text
Remember: what we have here are still delegates for deferred execution.
Delegates and Expression<...>
Expression<...> is not a class like any other class: it is a class that the compiler knows more about than on any common class. The Expression<...> class represents a lambda expression as the compiler sees it.
The aim of the Expression<...> class is to allow analyzing the lambda expression by C# code at runtime. This was needed to allow implementing any kind of data provider for Linq.
The C# language mandates that a variable of type Expression<...> can only hold a literal lambda expression. Not to be confused with assigning a delegate that might have been set to a lambda expression. The assignment is evaluated at compile time by the C# compiler (you remember: it is a class the compiler knows more about than of other common classes).
E.g. one can assign a lambda expression to a variable of type Expression<...>:
Expression<Action<string>> e = s => Console.WriteLine("{0}", s);
But one can not assign a delegate to that varialbe, e.g. this will not compile:
Action<string> print = s => Console.WriteLine("{0}", s);
Expression<Action<string>> e = print;
The error is:
Cannot implicitly convert type 'System.Action<string>' to 'System.Linq.Expressions.Expression<System.Action<string>>'
You would have to pass a lambda expression again:
Action<string> print = s => Console.WriteLine("{0}", s);
Expression<Action<string>> e = s=>print(s);
Execution of delegate versus execution of Expression
A delegate is executed by "calling" it with the needed parameters, e.g.
print = s => { Console.WriteLine("{0}", s); };
print("Hello...");
An expression has to be compiled before executed (if that was needed, but usually this is not the primary purpose of the using the Expression<...> class):
Expression<Action<string>> e = s => Console.WriteLine("{0}", s);
Action<string> action = e.Compile();
action("Hello...");
BTW: If execution of the lambda expression is the main purpose then delegates will do it. If analyzing the structure of the lambda expression, the Expression<...> class is the "tool of choice".
Properties and lambda expressions
Properties have a dominant role in the .Net framework. They are preferrably used in serialization and in all the declarative interface descriptions (WCF, WPF, etc.).
So, one might be tempted to construct some "magic" wrapper functions to pass "a reference to a property" and do some assignment to it and trigger some events, etc.
Since a lambda expression's body is like a function body (maybe with some syntactic sugar to abbreviate the writing of them), a lambda expression of the following form is calling the
getter of the property (This is
not a reference to the property.
Func<int> func = ()=>obj.Prop;
There is no way in C# to pass a reference of a property anywhere.
Now, why not passing an Expression<...> instance to the "magic" method? E.g.
public static void MagicFunction(Expression<Func<int>> prop)
{
}
MagicFunction(()=>obj.Prop);
Getting the name of the property is possible. It depends on the effort you put into making this robust (i.e. avoid abuse of the magic function with say
MagicFunction(()=>12);
).
Now, assigning to the property is also possible, but this is a semantic "non-sense":
you pass a getter to set the property.
The way was to get the propery by name from the given type or instance and call SetValue(...) on that.
public static void Set<C, V>(C obj, Expression<Func<V>> p, V v)
{
string name = p.Body.ToString().Split('.', '+', '/').Last();
typeof(C).GetProperty(name).SetValue(obj, v, null);
}
public static void Set<C, V>(Expression<Func<V>> p, V v)
{
Set<C, V>(default(C), p, v);
}
Usage:
Set<program,>(()=>StaticProp, 123);
Program obj = new Program();
Set(obj, ()=>obj.Prop, 234);
Summary
After my lengthly introduction and elaboration on delegates, lambda expressions and finally the Expression<...> class, the simple conclusion is that you
can do some magic stuff (which is hopefully not so magic any more). But just since you
could do it does not mean you
should do it.
I consider abusing the
Expression<...> and the
getter of a property to do an
assignment on the property is
too much of a stretch for semantic integrity.
Now that you got the background you might agree ;-)
Cheers
Andi
PS: Now I ended up writing a solution almost in extent and form of an article... ;-) Have fun!