提问人:Jon Kragh 提问时间:8/12/2010 最后编辑:EvilDrJon Kragh 更新时间:3/3/2023 访问量:175920
C# 检查属性属性是否为 null 的优雅方法
C# elegant way to check if a property's property is null
问:
在 C# 中,假设您要在此示例中提取一个值 和 ,并且都可以为 null。PropertyC
ObjectA
PropertyA
PropertyB
ObjectA.PropertyA.PropertyB.PropertyC
如何用最少的代码安全地获得?PropertyC
现在我会检查:
if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
// safely pull off the value
int value = objectA.PropertyA.PropertyB.PropertyC;
}
如果能做更多这样的事情(伪代码)就好了。
int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;
甚至可能因 null 合并运算符而进一步崩溃。
编辑最初我说我的第二个例子就像 js,但我把它改成了 psuedo-code,因为它被正确地指出它在 js 中不起作用。
答:
我会使用与 Nullable 类型类似的模式,在 PropertyA 类型(如果不是您的类型,则为扩展方法)中编写您自己的方法。
class PropertyAType
{
public PropertyBType PropertyB {get; set; }
public PropertyBType GetPropertyBOrDefault()
{
return PropertyB != null ? PropertyB : defaultValue;
}
}
评论
你这样做的方式是正确的。
您可以使用此处描述的技巧,使用 Linq 表达式:
int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);
但是手动检查每个属性要慢得多......
这是不可能的。
如果由于 null 取消引用而为 null,则将失败,这是一个错误。ObjectA.PropertyA.PropertyB
ObjectA
if(ObjectA != null && ObjectA.PropertyA
...由于短路而工作,即永远不会检查是否是。ObjectA.PropertyA
ObjectA
null
你提出的第一种方式是最好和最明确的意图。如果有的话,您可以尝试重新设计,而不必依赖这么多空值。
此代码是“最少的代码量”,但不是最佳实践:
try
{
return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
return null;
}
评论
你能在你的类中添加一个方法吗?如果没有,你有没有考虑过使用扩展方法?您可以为对象类型创建一个名为 的扩展方法。GetPropC()
例:
public static class MyExtensions
{
public static int GetPropC(this MyObjectType obj, int defaltValue)
{
if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
return obj.PropertyA.PropertyB.PropertyC;
return defaltValue;
}
}
用法:
int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)
顺便说一句,这假设你使用的是 .NET 3 或更高版本。
你可以这样做:
class ObjectAType
{
public int PropertyC
{
get
{
if (PropertyA == null)
return 0;
if (PropertyA.PropertyB == null)
return 0;
return PropertyA.PropertyB.PropertyC;
}
}
}
if (ObjectA != null)
{
int value = ObjectA.PropertyC;
...
}
或者更好的可能是这样的:
private static int GetPropertyC(ObjectAType objectA)
{
if (objectA == null)
return 0;
if (objectA.PropertyA == null)
return 0;
if (objectA.PropertyA.PropertyB == null)
return 0;
return objectA.PropertyA.PropertyB.PropertyC;
}
int value = GetPropertyC(ObjectA);
重构以遵守得墨忒耳定律
评论
假设您有类型的空值,一种方法是这样的:
var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;
我是 C# 的忠实粉丝,但在新 Java (1.7?) 中,一个非常好的事情是 .?算子:
var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
评论
您显然在寻找可为 Nullable 的单子:
string result = new A().PropertyB.PropertyC.Value;
成为
string result = from a in new A()
from b in a.PropertyB
from c in b.PropertyC
select c.Value;
如果任何可为 null 的属性为 null,则返回 ;否则,的值为 。null
Value
class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }
LINQ 扩展方法:
public static class NullableExtensions
{
public static TResult SelectMany<TOuter, TInner, TResult>(
this TOuter source,
Func<TOuter, TInner> innerSelector,
Func<TOuter, TInner, TResult> resultSelector)
where TOuter : class
where TInner : class
where TResult : class
{
if (source == null) return null;
TInner inner = innerSelector(source);
if (inner == null) return null;
return resultSelector(source, inner);
}
}
评论
SelectMany
from ... in ... from ... in ...
一旦你克服了 lambda gobbly-gook,这种方法就相当简单了:
public static TProperty GetPropertyOrDefault<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)
where TObject : class
{
try
{
return valueFunc.Invoke(model);
}
catch (NullReferenceException nex)
{
return default(TProperty);
}
}
其用法可能如下所示:
ObjectA objectA = null;
Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));
Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));
评论
var result = nullableproperty ?? defaultvalue;
(null-coalescing 运算符)表示,如果第一个参数是 ,则返回第二个参数。??
null
评论
当我需要像这样链接调用时,我依赖于我创建的帮助程序方法 TryGet():
public static U TryGet<T, U>(this T obj, Func<T, U> func)
{
return obj.TryGet(func, default(U));
}
public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
{
return obj == null ? whenNull : func(obj);
}
在您的情况下,您可以这样使用它:
int value = ObjectA
.TryGet(p => p.PropertyA)
.TryGet(p => p.PropertyB)
.TryGet(p => p.PropertyC, defaultVal);
评论
只是偶然发现了这篇文章。
前段时间,我在 Visual Studio Connect 上提出了一个关于添加新运算符的建议。???
这需要框架团队做一些工作,但不需要改变语言,而只是做一些编译器魔术。这个想法是编译器应该更改此代码(不允许语法 atm)
string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";
添加到此代码中
Func<string> _get_default = () => "no product defined";
string product_name = Order == null
? _get_default.Invoke()
: Order.OrderDetails[0] == null
? _get_default.Invoke()
: Order.OrderDetails[0].Product == null
? _get_default.Invoke()
: Order.OrderDetails[0].Product.Name ?? _get_default.Invoke()
对于 null 检查,这可能看起来像
bool isNull = (Order.OrderDetails[0].Product ??? null) == null;
您可以使用以下扩展,我认为它真的很好:
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
where TF : class
{
return t != null ? f(t) : default(TR);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
where T1 : class
where T2 : class
{
return Get(Get(p1, p2), p3);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
where T1 : class
where T2 : class
where T3 : class
{
return Get(Get(Get(p1, p2), p3), p4);
}
它的使用方式如下:
int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);
短扩展方法:
public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class where TInput : class
{
if (o == null) return null;
return evaluator(o);
}
用
PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);
这种简单的扩展方法以及您可以在 http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/ 上找到的更多内容
编辑:
使用了一会儿后,我认为此方法的正确名称应该是 IfNotNull() 而不是原始的 With()。
2014 年更新:C# 6 有一个新运算符,称为“安全导航”或“空传播”?.
parent?.child
长期以来,这是一个非常受欢迎的请求 https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d
我写了一个接受默认值的方法,以下是如何使用它:
var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");
代码如下:
public static class Helper
{
/// <summary>
/// Gets a property if the object is not null.
/// var teacher = new Teacher();
/// return teacher.GetProperty(t => t.Name);
/// return teacher.GetProperty(t => t.Name, "Default name");
/// </summary>
public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
{
if (item1 == null)
{
return defaultValue;
}
return getItem2(item1);
}
}
评论
在 C# 6 中,可以使用 Null 条件运算符。所以最初的测试将是:
int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
评论
value
PropertyC
PropertyB
Object A
null
if(propertyX == null) {value = null} else if (propertyY == null){ value = null} else if......
if(propertyZ != null) { value = propertyZ }
objectA == null || objectA.PropertyA == null || objectA.PropertyA.PropertyB == null ? null : objectA.PropertyA.PropertyB.PropertyC
我在新的 C# 6.0 中看到了一些东西, 这是通过使用“?”而不是检查
例如,而不是使用
if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{
var city = person.contact.address.city;
}
您只需使用
var city = person?.contact?.address?.city;
我希望它对某人有所帮助。
更新:
你现在可以这样做了
var city = (Person != null)?
((Person.Contact!=null)?
((Person.Contact.Address!= null)?
((Person.Contact.Address.City!=null)?
Person.Contact.Address.City : null )
:null)
:null)
: null;
教师 teacher = 新教师();
teacher.name = teacher.name == null ?默认值:teacher.name;
下一个:避免 null 引用异常
评论