C# 检查属性的属性是否为 null 的优雅方法

发布于 2024-09-14 05:28:49 字数 711 浏览 5 评论 0原文

在 C# 中,假设您想要从本示例中的 PropertyC 以及 ObjectAPropertyAPropertyB 中获取值> 都可以为空。

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;

空合并运算符可能会进一步崩溃。

编辑 最初我说我的第二个例子就像js,但我将其更改为伪代码,因为它被正确指出它在js中不起作用。

In C#, say that you want to pull a value off of PropertyC in this example and ObjectA, PropertyA and PropertyB can all be null.

ObjectA.PropertyA.PropertyB.PropertyC

How can I get PropertyC safely with the least amount of code?

Right now I would check:

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
    // safely pull off the value
    int value = objectA.PropertyA.PropertyB.PropertyC;
}

It would be nice to do something more like this (pseudo-code).

int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;

Possibly even further collapsed with a null-coalescing operator.

EDIT Originally I said my second example was like js, but I changed it to psuedo-code since it was correctly pointed out that it would not work in js.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

凉宸 2024-09-21 05:28:49

在 C# 6 中,您可以使用 空条件运算符。所以原来的测试将是:

int? value = objectA?.PropertyA?.PropertyB?.PropertyC;

In C# 6 you can use the Null Conditional Operator. So the original test will be:

int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
困倦 2024-09-21 05:28:49

简短的扩展方法:

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()。

Short Extension Method:

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);
}

Using

PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);

This simple extension method and much more you can find on http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

EDIT:

After using it for moment I think the proper name for this method should be IfNotNull() instead of original With().

变身佩奇 2024-09-21 05:28:49

你能在你的类中添加一个方法吗?如果没有,您是否考虑过使用扩展方法?您可以为您的对象类型创建一个名为 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 或更高版本。

Can you add a method to your class? If not, have you thought about using extension methods? You could create an extension method for your object type called GetPropC().

Example:

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;
    }
}

Usage:

int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)

By the way, this assumes you are using .NET 3 or higher.

酒儿 2024-09-21 05:28:49

你这样做的方式是正确的。

可以使用类似于这里,使用 Linq 表达式:

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

但是手动检查每个属性要慢得多...

The way you're doing it is correct.

You could use a trick like the one described here, using Linq expressions :

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

But it's much slower that manually checking each property...

咋地 2024-09-21 05:28:49

重构以遵守德墨忒尔定律

Refactor to observe the Law of Demeter

淡淡離愁欲言轉身 2024-09-21 05:28:49

显然,您正在寻找 Nullable Monad

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);
    }
}

You're obviously looking for the Nullable Monad:

string result = new A().PropertyB.PropertyC.Value;

becomes

string result = from a in new A()
                from b in a.PropertyB
                from c in b.PropertyC
                select c.Value;

This returns null, if any of the nullable properties are null; otherwise, the value of Value.

class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }

LINQ extension methods:

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);
    }
}
瑾兮 2024-09-21 05:28:49

假设您有类型的空值,一种方法是:

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

我是 C# 的忠实粉丝,但新 Java(1.7?)中的一个非常好的东西是 .?操作员:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;

Assuming you have empty values of types one approach would be this:

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

I'm a big fan of C# but a very nice thing in new Java (1.7?) is the .? operator:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
一曲爱恨情仇 2024-09-21 05:28:49

我在新的 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;

I saw something in the new C# 6.0,
this is by using '?' instead of checking

for example instead of using

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{ 
  var city = person.contact.address.city;
}

you simply use

var city = person?.contact?.address?.city;

I hope it helped somebody.


UPDATE:

You could do like this now

 var city = (Person != null)? 
           ((Person.Contact!=null)? 
              ((Person.Contact.Address!= null)?
                      ((Person.Contact.Address.City!=null)? 
                                 Person.Contact.Address.City : null )
                       :null)
               :null)
            : null;
嘿嘿嘿 2024-09-21 05:28:49

这段代码是“最少的代码量”,但不是最佳实践:

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}

This code is "the least amount of code", but not the best practice:

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}
薄荷→糖丶微凉 2024-09-21 05:28:49

当我需要像这样链接调用时,我依赖于我创建的辅助方法 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);

When I need to chain calls like that, I rely on a helper method I created, 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);
    }

In your case, you would use it like so:

    int value = ObjectA
        .TryGet(p => p.PropertyA)
        .TryGet(p => p.PropertyB)
        .TryGet(p => p.PropertyC, defaultVal);
2024-09-21 05:28:49

你可以这样做:

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);

You could do this:

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;
    ...
}

Or even better might be this:

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);
小清晰的声音 2024-09-21 05:28:49

我会使用与 Nullable 类型类似的模式以 PropertyA 类型(如果不是您的类型,则为扩展方法)编写您自己的方法。

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}

I would write your own method in the type of PropertyA (or an extension method if it's not your type) using the similar pattern to the Nullable type.

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}
行至春深 2024-09-21 05:28:49

你可以使用以下扩展,我认为它非常好:

/// <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);

you can use the following extension and I think it is really good:

/// <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);
}

And it is used like this:

int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);
懷念過去 2024-09-21 05:28:49

一旦你克服了 lambda 的繁琐,这种方法就相当简单了:

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));

This approach is fairly straight-forward once you get over the 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);
        }
    }

With usage that might look like:

ObjectA objectA = null;

Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));

Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));
海夕 2024-09-21 05:28:49

刚刚偶然发现了这篇文章。

不久前,我在 Visual Studio Connect 上提出了关于添加新的 ??? 运算符的建议。

http ://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

这需要框架团队做一些工作,但是不需要改变语言,只需执行一些编译器魔法即可。这个想法是编译器应该将此代码(不允许 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;

Just stumbled accross this post.

Some time ago I made a suggestion on Visual Studio Connect about adding a new ??? operator.

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

This would require some work from the framework team but don't need to alter the language but just do some compiler magic. The idea was that the compiler should change this code (syntax not allowed atm)

string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";

into this code

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()

For null check this could look like

bool isNull = (Order.OrderDetails[0].Product ??? null) == null;
放赐 2024-09-21 05:28:49

我编写了一个接受默认值的方法,以下是如何使用它:

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);
    }
}

I wrote a method that accepts a default value, here is how to use it:

var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");

Here is the code:

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);
    }
}
捶死心动 2024-09-21 05:28:49

这是不可能的。
如果 ObjectA 由于 null 取消引用而为 null,则 ObjectA.PropertyA.PropertyB 将失败,这是一个错误。

if(ObjectA != null && ObjectA.PropertyA ... 由于短路而起作用,即 ObjectA.PropertyA 如果 ObjectA< 则永远不会被检查/code> 是 null

您建议的第一种方法是最好的,也是最明确的,您可以尝试重新设计,而不必依赖这么多空值。

It is not possible.
ObjectA.PropertyA.PropertyB will fail if ObjectA is null due to null dereferencing, which is an error.

if(ObjectA != null && ObjectA.PropertyA ... works due to short circuiting, ie ObjectA.PropertyA will never be checked if ObjectA is null.

The first way you propose is the best and most clear with intent. If anything you could try to redesign without having to rely on so many nulls.

携君以终年 2024-09-21 05:28:49

教师 教师 = new Teacher();

老师.name = 老师.name == null ?默认值:教师.姓名;

Teacher teacher = new Teacher();

teacher.name = teacher.name == null ? defaultvalue : teacher.name;

我的黑色迷你裙 2024-09-21 05:28:49
var result = nullableproperty ?? defaultvalue;

??(空合并运算符)意味着如果第一个参数为 null,则返回第二个参数。

var result = nullableproperty ?? defaultvalue;

The ?? (null-coalescing operator) means if the first argument is null, return the second one instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文