Comments 15
Если метод экземплярный то в поле target записывается ссылка на экземпляр-владелец метода (он нужен нам, ведь если метод экземплярный то это как минимум подразумевает работу с полями этого объекта target), а в methodPtr ссылка на метод. Если метод статический то записываются в поля target и methodPtr будут записаны null и ссылка на метод соответственно.
Для статических методов можно точно так же указывать target. В таком случае он станет первым аргументом метода (соответственно, у метода должно быть на один параметр больше, чем в сигнатуре делегата).
Этой возможностью компилятор C# пользуется при создании делегатов на методы-расширения. Также ее удобно использовать совместно с компиляцией деревьев выражений через CompileToMethod, потому что методы экземпляров там толком не поддерживаются.
Также не могу не напомнить, что лямбда-выражение не обязано быть делегатом, оно может быть и типа Expression<>
В обратную сторону тоже работает и экземплярные методы можно использовать как статические (без сохранения target). А при вызове передавать объект первым параметром.
А можно поподробнее? А То я в свое время велосипедил трамплин, что не очень удобно:
public static MethodInfo CompileToInstanceMethod(this LambdaExpression expression, TypeBuilder tb, string methodName)
{
var paramTypes = expression.Parameters.Select(x => x.Type).ToArray();
var proxyParamTypes = new Type[paramTypes.Length - 1];
Array.Copy(paramTypes, 1, proxyParamTypes, 0, proxyParamTypes.Length);
var proxy = tb.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Virtual, expression.ReturnType, proxyParamTypes);
var method = tb.DefineMethod($"<{proxy.Name}>__StaticProxy", MethodAttributes.Private | MethodAttributes.Static, proxy.ReturnType, paramTypes);
expression.CompileToMethod(method);
proxy.GetILGenerator().EmitCallWithParams(method, paramTypes.Length);
return proxy;
}
Странное у вас какое-то решение. У меня не получалось заставить LambdaExpression иметь TypeBuilder первым аргументом...
А с делегатом — все просто. Если вам нужна не реализация какого-то интерфейса или переопределение виртуального метода, а просто метод чтобы вызвать его через делегат — то этот метод объявляется в статическом классе, а контекст передается ему первым параметром.
Не знаю, что странного, вроде со своей задачей справлялся. Правда, потом переносил на Core, а там выпилен CompileToMethod, к сожалению. Обсуждается его возврат, но покааа там разберутся…
В такой формулировке знал, но изначально подумал, что чего-то я пропустил в этой жизни и можно сильно упростить себе жизнь. Но — нет :)
Func<>
и Action<>
— это такие же делегаты, вот их определение из referencesource:
public delegate void Action();
public delegate void Action<in T>(T obj);
public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2);
public delegate void Action<in T1,in T2,in T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<in T1,in T2,in T3,in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
Но лучше их использовать только там, где смысл их аргументов понятен из контекста. Пользовательский делегат позволяет указать имена для параметров — а это важная часть самодокументируемого кода.
А еще Func<>
и Action<>
не могут иметь ref или out-параметров, а также для их параметров нельзя указать пользовательские атрибуты.
А за статью спасибо, содержательная статья.
Получается довольно бессмысленно, но работает.
void MyFunc(myDelegate deleg, int arg){deleg.Invoke(arg);}
Очень даже не бессмысленно.
Пусть нам нужно выполнить тяжёлую обработку большого набора данных. Такой метод может распараллеливать вычисления автоматически между потоками (PLINQ, Parallel.ForEach) или компьютерами (Ignite):
IEnumerable<TRes> Apply<TArg, TRes>(Func<TArg, TRes> func, IEnumerable<TArg> args)
P.S. .Net -> .NET
CS1643 Not all code paths return a value in lambda expressionMyDeleg myDeleg = (string x) => { x + "world!"};
Правило простое, если лямбду поместить в { } (блок кода?), например, то она автоматически становится анонимной функцией и снова требует ключевое слово return:
MyDeleg @delegate = x => { return x + "world!"; };
Простенько, но со вкусом.
Я то думал тут будет действительно шпаргалка, а тут целая глава Рихтера:). Спасибо за статью, повторение мать учения!
Делегаты и Лямбда выражения в C# .Net — Шпаргалка или коротко о главном