C#表达式内部结构
C# Expression internal structure
我正在尝试了解 表达式 class 的工作原理。
一个简单的例子:
Expression<Func<string, bool>> exp = p => p.Contains("x");
为什么我没有收到类似“您不能转换为 Expression> 因为它不是委托”这样的错误?
我尝试调试此代码,VS 将我移至此方法
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);
}
是微软的魔法吗?我没有找到任何覆盖转换器或其他东西,谁调用了 Parameter 方法?他们是否在编译器中编写了一些特殊代码来编译 Expression class 而不是任何其他“标准”class?
是的,您可以称之为魔法,因为编译器会在幕后为您做很多提升。那么让我们看看最简单的程序会发生什么。我刚刚创建了一个控制台应用程序并添加了您的代码。
internal class Program {
private static void Main(string[] args) {
Expression<Func<string, bool>> exp = p => p.Contains("x");
}
}
我编译了一下,看了看IL代码。 (可以跳过这部分)
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
而这段 IL 代码大致翻译为
// 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);
这是代码的第一行,您可以看到正在调用 public static ParameterExpression Parameter(Type type, string name)
。
我正在尝试了解 表达式 class 的工作原理。
一个简单的例子:
Expression<Func<string, bool>> exp = p => p.Contains("x");
为什么我没有收到类似“您不能转换为 Expression
我尝试调试此代码,VS 将我移至此方法
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);
}
是微软的魔法吗?我没有找到任何覆盖转换器或其他东西,谁调用了 Parameter 方法?他们是否在编译器中编写了一些特殊代码来编译 Expression class 而不是任何其他“标准”class?
是的,您可以称之为魔法,因为编译器会在幕后为您做很多提升。那么让我们看看最简单的程序会发生什么。我刚刚创建了一个控制台应用程序并添加了您的代码。
internal class Program {
private static void Main(string[] args) {
Expression<Func<string, bool>> exp = p => p.Contains("x");
}
}
我编译了一下,看了看IL代码。 (可以跳过这部分)
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
而这段 IL 代码大致翻译为
// 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);
这是代码的第一行,您可以看到正在调用 public static ParameterExpression Parameter(Type type, string name)
。