I'm trying to understand how Expression class work.
A simple example:
Expression<Func<string, bool>> exp = p => p.Contains("x");
Why am I not getting an error like "you cannot convert to Expression<Func<string, bool>> because it isn't delegate" ?
I tried to debug this code and VS moves me to this method
public static ParameterExpression Parameter(Type type, string name)
{
Validate(type, allowByRef: true);
bool byref = type.IsByRef;
if (byref)
{
type = type.GetElementType();
}
return ParameterExpression.Make(type, name, byref);
}
Is it Microsoft magic? I didn't find any overrode converter or something else and who called Parameter method? Did they write some special code in the compiler to compile Expression class not as any other "standard" class?
Yes, you may call it magic because the compiler will do a lot of lifting for you behind scenes. So let us see what happens with the simplest program. I just created a Console Application and added your code.
internal class Program {
private static void Main(string[] args) {
Expression<Func<string, bool>> exp = p => p.Contains("x");
}
}
I compiled it and looked at the IL code. (you may skip this part if you like)
IL_0000: ldtoken [mscorlib/*23000001*/]System.String/*01000021*/
IL_0005: call class [mscorlib/*23000001*/]System.Type/*01000014*/ [mscorlib/*23000001*/]System.Type/*01000014*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000022*/)/*0A000014*/
IL_000a: ldstr "p"
IL_000f: call class [System.Core/*23000002*/]System.Linq.Expressions.ParameterExpression/*0100001D*/ [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/::Parameter(class [mscorlib/*23000001*/]System.Type/*01000014*/, string)/*0A000040*/
IL_0014: stloc.0 // V_0
IL_0015: ldloc.0 // V_0
IL_0016: ldtoken method instance bool [mscorlib/*23000001*/]System.String/*01000021*/::Contains(string)/*0A000041*/
IL_001b: call class [mscorlib/*23000001*/]System.Reflection.MethodBase/*0100002E*/ [mscorlib/*23000001*/]System.Reflection.MethodBase/*0100002E*/::GetMethodFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeMethodHandle/*0100002F*/)/*0A000042*/
IL_0020: castclass [mscorlib/*23000001*/]System.Reflection.MethodInfo/*01000017*/
IL_0025: ldc.i4.1
IL_0026: newarr [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/
IL_002b: dup
IL_002c: ldc.i4.0
IL_002d: ldstr "x"
IL_0032: ldtoken [mscorlib/*23000001*/]System.String/*01000021*/
IL_0037: call class [mscorlib/*23000001*/]System.Type/*01000014*/ [mscorlib/*23000001*/]System.Type/*01000014*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000022*/)/*0A000014*/
IL_003c: call class [System.Core/*23000002*/]System.Linq.Expressions.ConstantExpression/*01000030*/ [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/::Constant(object, class [mscorlib/*23000001*/]System.Type/*01000014*/)/*0A000043*/
IL_0041: stelem.ref
IL_0042: call class [System.Core/*23000002*/]System.Linq.Expressions.MethodCallExpression/*01000031*/ [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/::Call(class [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/, class [mscorlib/*23000001*/]System.Reflection.MethodInfo/*01000017*/, class [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/[])/*0A000044*/
IL_0047: ldc.i4.1
IL_0048: newarr [System.Core/*23000002*/]System.Linq.Expressions.ParameterExpression/*0100001D*/
IL_004d: dup
IL_004e: ldc.i4.0
IL_004f: ldloc.0 // V_0
IL_0050: stelem.ref
IL_0051: call class [System.Core/*23000002*/]System.Linq.Expressions.Expression`1/*01000032*/<!!0/*class [mscorlib*//*23000001*//*]System.Func`2*//*01000012*//*<string, bool>*/> [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/::Lambda<class [mscorlib/*23000001*/]System.Func`2/*01000012*/<string, bool>>(class [System.Core/*23000002*/]System.Linq.Expressions.Expression/*0100002D*/, class [System.Core/*23000002*/]System.Linq.Expressions.ParameterExpression/*0100001D*/[])/*2B000002*/
IL_0056: pop
And this IL Code roughly translates as
// Define Expresin parameter p
var parameterExpression = Expression.Parameter(typeof (string), "p");
// Gets method handler for string.Contains method
// Plase note that __methodref can not be used in c# butit is valid in il code
var methodInfo = (MethodInfo) MethodBase.GetMethodFromHandle(__methodref (string.Contains));
// Constant string x added as paramter
var constantXString = new Expression[] {Expression.Constant("x", typeof (string))};
// Call given method on the the given parameter and make it into a lambda expression
Expression.Lambda<Func<string, bool>>(Expression.Call(parameterExpression, methodInfo, constantXString), parameterExpression);
It is the first line of the code where you can see that public static ParameterExpression Parameter(Type type, string name)
is being called.