Я собирался связать Expression.Lambda программно (из-за параметров неизвестного/переменного типа) и обнаружил, что если целевой метод использует params, отраженный вызов немного отличается от прямого вызова.
Во-первых, в официальной документации "подпись" (хотя это больше похоже на XML-документ):
public static
System.Linq.Expressions.Expression<TDelegate>
Lambda<TDelegate>(
System.Linq.Expressions.Expression body,
params System.Linq.Expressions.ParameterExpression[]? parameters
);
Обратите внимание, что второй параметр помечен как необязательный, и мы можем написать Expression.Lambda<T>(...). Если в Visual Studio перейти к дизассемблированному списку объявлений, то можно увидеть это:
public static
Expression<TDelegate>
Lambda<TDelegate>(
Expression body,
params ParameterExpression[] parameters
);
2-й параметр больше не помечен как опция. Теперь, когда я попытался вызвать этот метод, используя отражение как:
var lambdaFactory = typeof(Expression)
.GetMethods()
// Filter overloads
.Single(x => x.ToString() == "System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate](System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])")
.MakeGenericMethod(myTypeArgument);
var lambda = (LambdaExpression) lambdaFactory.Invoke(
null,
new object[] {
...
// ,null
}
);
, я получил TargetParameterCountException: Parameter count mismatch.. Но если добавить null в качестве еще одного параметра, все работает отлично.
Это немного странно для меня. Почему в MS Docs используется ? (необязательный маркер)? Является ли аргумент params необязательным, как и аргументы обычных опций, например string Foo(int a = 1); var result = Foo();? Или это просто синтаксический сахар? Вот почему я могу вызвать Expression.Lambda<T>(...) напрямую в редакторе, но скомпилированный код может быть другим (что также совместимо с системой отражения). Если да, значит ли это, что метод всегда получает значение null, даже если я не указываю значения? Но если метод использует аргумент params и ничего не передается, аргумент из тела метода является допустимым массивом с .Count == 0, а не null. Безопасно ли передавать null с помощью отражения, или мне следует создать пустой массив объектов?





? в документах обозначает ссылочный тип, допускающий значение NULL, а не необязательный параметр. Ссылочные типы, допускающие значение NULL, — это чисто «проверка во время компиляции», и во время выполнения они ничем не отличаются от обычных старых ссылочных типов.:
Ссылочные типы, допускающие значение NULL, — это не новые типы классов, а скорее аннотации к существующим ссылочным типам. Компилятор использует эти аннотации, чтобы помочь вам найти потенциальные ошибки с нулевыми ссылками в вашем коде. Нет никакой разницы во время выполнения между ссылочным типом, не допускающим значение NULL, и ссылочным типом, допускающим значение NULL. Компилятор не добавляет никаких проверок во время выполнения для ссылочных типов, не допускающих значение NULL. Преимущества заключаются в анализе времени компиляции. Компилятор генерирует предупреждения, которые помогают найти и исправить потенциальные нулевые ошибки в коде. Вы объявляете свое намерение, и компилятор предупреждает вас, когда ваш код нарушает это намерение.
Это объясняет, почему ? исчезает, когда вы смотрите на «дизассемблированный список объявлений».
Все, что ? говорит вам, это то, что вы можете передать null этому параметру, и метод все равно будет работать.
В этом случае альтернативой передаче null является передача пустого массива ParameterExpressions:
var lambda = (LambdaExpression) lambdaFactory.Invoke(
null,
new object[] {
someBodyExpression,
new ParameterExpression[] { }
}
);
Это будет отражать то, как вы называете Lambda нерефлексивно:
// I'm passing no extra arguments, so an empty array is passed to the "params" parameter
Lambda<T>(someBodyExpression)
Ха, я совсем забыл об этом. :D Спасибо.