Es ist möglich, schreiben, aber ich bin mir nicht sicher, ob ich es empfehlen würde. Hier ist etwas, das Sie nützlich finden können: Es gibt keinen booleschen Wert zurück, sondern stattdessen den Blattwert des Ausdrucks (keine Nullreferenz).
public static class Dereferencer
{
private static readonly MethodInfo safeDereferenceMethodInfo
= typeof (Dereferencer).GetMethod("SafeDereferenceHelper", BindingFlags.NonPublic| BindingFlags.Static);
private static TMember SafeDereferenceHelper<TTarget, TMember>(TTarget target,
Func<TTarget, TMember> walker)
{
return target == null ? default(TMember) : walker(target);
}
public static TMember SafeDereference<TTarget, TMember>(this TTarget target, Expression<Func<TTarget, TMember>> expression)
{
var lambdaExpression = expression as LambdaExpression;
if (lambdaExpression == null)
return default(TMember);
var methodCalls = new Queue<MethodCallExpression>();
VisitExpression(expression.Body, methodCalls);
var callChain = methodCalls.Count == 0 ? expression.Body : CombineMethodCalls(methodCalls);
var exp = Expression.Lambda(typeof (Func<TTarget, TMember>), callChain, lambdaExpression.Parameters);
var safeEvaluator = (Func<TTarget, TMember>) exp.Compile();
return safeEvaluator(target);
}
private static Expression CombineMethodCalls(Queue<MethodCallExpression> methodCallExpressions)
{
var callChain = methodCallExpressions.Dequeue();
if (methodCallExpressions.Count == 0)
return callChain;
return Expression.Call(callChain.Method,
CombineMethodCalls(methodCallExpressions),
callChain.Arguments[1]);
}
private static MethodCallExpression GenerateSafeDereferenceCall(Type targetType,
Type memberType,
Expression target,
Func<ParameterExpression, Expression> bodyBuilder)
{
var methodInfo = safeDereferenceMethodInfo.MakeGenericMethod(targetType, memberType);
var lambdaType = typeof (Func<,>).MakeGenericType(targetType, memberType);
var lambdaParameterName = targetType.Name.ToLower();
var lambdaParameter = Expression.Parameter(targetType, lambdaParameterName);
var lambda = Expression.Lambda(lambdaType, bodyBuilder(lambdaParameter), lambdaParameter);
return Expression.Call(methodInfo, target, lambda);
}
private static void VisitExpression(Expression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
VisitMemberExpression((MemberExpression) expression, methodCallsQueue);
break;
case ExpressionType.Call:
VisitMethodCallExpression((MethodCallExpression) expression, methodCallsQueue);
break;
}
}
private static void VisitMemberExpression(MemberExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Expression.Type,
expression.Type,
expression.Expression,
p => Expression.PropertyOrField(p, expression.Member.Name));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Expression, methodCallsQueue);
}
private static void VisitMethodCallExpression(MethodCallExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Object.Type,
expression.Type,
expression.Object,
p => Expression.Call(p, expression.Method, expression.Arguments));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Object, methodCallsQueue);
}
}
Sie können es auf diese Weise verwenden:
var street = person.SafeDereference(d=>d.addressdetails.Street);
street = person.SafeDereference(d=>d.addressdetails[1].Street);
street = person.SafeDereference(d=>d.addressdetails.FirstOrDefault().Street);
var name = person.SafeDereference(d=>d.InvoiceList.FirstOrDefault().Product.Name);
Achtung: Dies ist nicht vollständig getestet, es sollte Arbeit mit Methoden und Eigenschaften, aber wahrscheinlich nicht mit Erweiterungsmethoden innerhalb des Ausdrucks.
Edit: Ok, es kann keine Erweiterung Methoden für jetzt (z. B.), aber es ist immer noch möglich, die Lösung anzupassen.
eine Reflexion von Eigenschaften und Werten des Objekts erhalten und foreach auf, dass die Erweiterung Methode? Wenn Sie eine Null erhalten, behandeln Sie es wie Sie wollen. Sie können das Objekt so oft ändern, wie Sie möchten, und es wird immer funktionieren. Es wird nicht empfohlen, Code, sondern Reflektionen zu verwenden. Es sei denn, es ist absolut notwendig. Für den Lichtgebrauch ist es in Ordnung, aber wenn es sich tausendfach widerspiegelt, müssen Sie Ihre Modelle überdenken. – ppumkin