Stack Overflow на русском Asked by iluxa1810 on January 4, 2021
Допустим, я хочу принимать на вход Expression<Func<T, TProp>> expr
, где будет указано поле с которым нужно что-то сделать.
Все бы хорошо, но если я получаю свойство, которое находится во внутреннем объекте и оно равно null, то я вылетаю.
Например, x=>x.Prop1.Prop2.Prop3
На вопросики в выражении, компилятор ругается…:
An expression tree lambda may not contain a null propagating operator
Я как-нибудь могу, получив данный Expression динамически его модифицировать?
За проверку на null и вообще за придание смысла вызову свойства в коде отвечает провайдер. Если ваш окончательный провайдер - это обычный LINQ to Objects, т.е. если вы просто компилируете это выражение в Func<SomeB, int?>
- напишите просто Func<SomeB, int?>
, выражения вам не нужны.
LINQ to Objects все равно, отдаете вы ему Expression, или делегат - он все равно Expression в этот делегат скомпилирует, отдать сразу делегат проще и дешевле.
Если у вас нужны именно Expression, т.е. предполагается некая динамическая работа с этим деревом выражений до того, как оно будет скомпилировано LINQ to Objects, то добавьте больше динамики в эту работу, дополнительным визитором. Допишите проверки на null на лету. Примерно вот так:
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
namespace ConsoleApp4
{
class SomeA
{
public int MyProperty { get; set; }
}
class SomeB
{
public SomeA SomeA { get; set; }
}
class NullPropagationVisitor : ExpressionVisitor
{
[return: NotNullIfNotNull("node")]
public override Expression Visit(Expression node)
{
return base.Visit(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType == MemberTypes.Property)
{
return WrapInConditional(node);
}
return base.VisitMember(node);
}
private Expression WrapInConditional(MemberExpression node)
{
var targetType = node.Type;
bool addConvert = false;
if (targetType.IsValueType)
{
targetType = typeof(Nullable<>).MakeGenericType(targetType);
addConvert = true;
}
var nullCheck = Expression.Equal(node.Expression, Expression.Constant(null));
var isNull = Expression.Constant(null, targetType);
var visitedNode = base.VisitMember(node);
Expression isNotNull = addConvert ? Expression.Convert(visitedNode, targetType) : visitedNode;
return Expression.Condition(nullCheck, isNull, isNotNull);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Convert)
{
return Visit(node.Operand);
}
return base.VisitUnary(node);
}
}
class Program
{
static void Main(string[] args)
{
Expression<Func<SomeB, int?>> ex = someb => someb.SomeA.MyProperty;
var result = ((Expression<Func<SomeB, int?>>)new NullPropagationVisitor().Visit(ex)).Compile();
Console.WriteLine(result(new SomeB { }) == null); // True, null
Console.WriteLine(result(new SomeB { SomeA = new SomeA { MyProperty = 42 } })); // 42
}
}
}
Answered by PashaPash on January 4, 2021
Может такой вариант
(x=>{ try { return x.Prop1.Prop2.Prop3 } catch { return null;}})
элегантнее возможно это
(myLambda)
myLambda = () => ....
Answered by Aziz Umarov on January 4, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP