是否有方便的语法在访问空对象的属性时返回空而不是异常
本文关键字:返回 异常 属性 对象 语法 访问 是否 方便 | 更新日期: 2023-09-27 18:15:39
考虑这个简单的c#示例:
var person = new Person {Name = "Fred", MailingAddress=null };
var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street);
显然这将抛出NullReferenceException,因为MailingAddress属性是空的。
我可以将第二行重写为:
var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street);
有更简单的表达方式吗?
这段代码在技术上违反了得墨忒耳定律,所以有些人会认为首先写这段代码是不好的形式。
所以,不,没有原生语法来完成你想要的,但是将这些代码移动到你的Person
类的属性中会使调用代码更干净,也使你符合得米忒律。
public string StreetAddress{
get { return this.MailingAddress == null ?
(String)null : person.MailingAddress.Street; }
}
没有合适的语法。合并运算符是其中的一部分,但是您需要处理在中遍历 null,而不仅仅是替换null。您可以做的一件事是为类设置一个静态的"null对象",类似于:
public class Address
{
public static Address Null = new Address();
// Rest of the class goes here
}
那么你可以像这样使用合并运算符:
(person.MailingAddress ?? Address.Null).Street
如果你想使用扩展方法,你可以这样做:
public static class NullExtension
{
public static T OrNew<T>(this T thing)
where T: class, new()
{
return thing ?? new T();
}
}
那么你可以这样做:
(person.MailingAddress.OrNew().Street)
您可以使用基于表达式树的设备,因此您可以写
var n = person.NullPropagate(p => p.Contact.MailingAddress.StreetAddress.Number);
/* having the effect of:
(person == null)
? defaultValue
: (person.Contact == null)
? defaultValue
: (person.Contact.MailingAddress == null)
? defaultValue
: (person.Contact.MailingAddress.StreetAddress == null)
? defaultValue
: person.Contact.MailingAddress.StreetAddress.Number;
*/
免责声明:我没有写这段代码,我只是不知道我最初在哪里找到它。有人认识这个帮手吗?
public static R NullPropagate<T, R>(this T source, Expression<Func<T, R>> expression, R defaultValue)
{
var safeExp = Expression.Lambda<Func<T, R>>(
WrapNullSafe(expression.Body, Expression.Constant(defaultValue)),
expression.Parameters[0]);
var safeDelegate = safeExp.Compile();
return safeDelegate(source);
}
private static Expression WrapNullSafe(Expression expr, Expression defaultValue)
{
Expression obj;
Expression safe = expr;
while (!IsNullSafe(expr, out obj))
{
var isNull = Expression.Equal(obj, Expression.Constant(null));
safe = Expression.Condition (isNull, defaultValue, safe);
expr = obj;
}
return safe;
}
private static bool IsNullSafe(Expression expr, out Expression nullableObject)
{
nullableObject = null;
if (expr is MemberExpression || expr is MethodCallExpression)
{
Expression obj;
MemberExpression memberExpr = expr as MemberExpression;
MethodCallExpression callExpr = expr as MethodCallExpression;
if (memberExpr != null)
{
// Static fields don't require an instance
FieldInfo field = memberExpr.Member as FieldInfo;
if (field != null && field.IsStatic)
return true;
// Static properties don't require an instance
PropertyInfo property = memberExpr.Member as PropertyInfo;
if (property != null)
{
MethodInfo getter = property.GetGetMethod();
if (getter != null && getter.IsStatic)
return true;
}
obj = memberExpr.Expression;
}
else
{
// Static methods don't require an instance
if (callExpr.Method.IsStatic)
return true;
obj = callExpr.Object;
}
// Value types can't be null
if (obj.Type.IsValueType)
return true;
// Instance member access or instance method call is not safe
nullableObject = obj;
return false;
}
return true;
}
您可以使用这个扩展方法:
public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
where TInput : class
{
return (value != null) ? evaluator(value) : failureValue;
}
的例子:
person.MailingAddress.MayBe(p=>p.Street,default(Street))