#c# #expression-trees
#c# #деревья выражений
Вопрос:
Я пытаюсь понять, как работает класс Expression.
Простой пример:
Expression<Func<string, bool>> exp = p => p.Contains("x");
Почему я не получаю сообщение об ошибке типа «вы не можете преобразовать в выражение<Func<string, bool>>, потому что оно не делегируется»?
Я попытался отладить этот код, и 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);
}
Это магия Microsoft? Я не нашел никакого переопределенного конвертера или чего-то еще, и кто вызвал метод параметра? Они написали какой-то специальный код в компиляторе для компиляции класса Expression, отличного от любого другого «стандартного» класса?
Ответ №1:
Да, вы можете назвать это волшебством, потому что компилятор сделает для вас много работы за кулисами. Итак, давайте посмотрим, что происходит с простейшей программой. Я только что создал консольное приложение и добавил ваш код.
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)
вызывается.
Комментарии:
1. ПРИМЕЧАНИЕ. Я написал библиотеку , которая принимает дерево выражений и генерирует строку вызовов заводских методов, необходимых для создания того же дерева выражений .
2. @ZevSpitz NB ты тоже. Позвольте мне взглянуть на это
3. @ZevSpitz довольно аккуратно! мне нравится