访问 null 对象的属性时是否有方便的语法返回 null 而不是异常

发布于 2024-12-18 05:00:38 字数 475 浏览 0 评论 0原文

考虑这个简单的 C# 示例:

var person = new Person {Name = "Fred", MailingAddress=null };
var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street);

显然这会抛出 NullReferenceException,因为 MailingAddress 属性为 null。

我可以将第二行重写为:

var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street);

有没有更简单的方法来表达这一点?

Consider this simple c# example:

var person = new Person {Name = "Fred", MailingAddress=null };
var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street);

clearly this will throw a NullReferenceException because the MailingAddress proptery is null.

I could rewrite the second line as:

var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street);

Is there a simpler way to say express this?

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

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

发布评论

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

评论(4

予囚 2024-12-25 05:00:38

从技术上讲,此代码违反了德米特定律,因此有些人会认为编写这种形式很糟糕首先。

所以不,没有本地语法来完成你想要的,但是将此代码移动到 Person 类中的属性将使此调用代码更干净,并且还使你符合 demeter 定律。

public string StreetAddress{
   get { return this.MailingAddress == null ? 
                   (String)null : person.MailingAddress.Street; }
}

This code is technically a violation of the Law of Demeter, so some would consider it bad form to write this in the first place.

So no, there's no native syntax to accomplish what you want, but moving this code to a property in your Person class would make this calling code more clean, and also bring you in line with the law of demeter.

public string StreetAddress{
   get { return this.MailingAddress == null ? 
                   (String)null : person.MailingAddress.Street; }
}
水溶 2024-12-25 05:00:38

实际上没有任何好的语法。合并运算符是其中的一部分,但您需要处理通过 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)

There's not really any good syntax for this. The coalesce operator is part of it, but you need to handle traversing through a null, not just replacing a null. One thing you could do would be to have a static "null object" for the class, something like:

public class Address 
{
  public static Address Null = new Address();
  // Rest of the class goes here
}

Then you could use the coalesce operator like so:

(person.MailingAddress ?? Address.Null).Street

If you want to go the extension method route, you could do something like this:

public static class NullExtension
{
    public static T OrNew<T>(this T thing)
        where T: class, new()
    {
        return thing ?? new T();
    }
}

Then you could do:

(person.MailingAddress.OrNew().Street)
如梦初醒的夏天 2024-12-25 05:00:38

可以使用基于表达式树的设备,因此您可以编写

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

You could use a device based on expression trees, so you'd write

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

Disclaimer: I haven't written this code, I just don't know where I originally found it either. Anyone recognize this helper?

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;
}
家住魔仙堡 2024-12-25 05:00:38

您可以使用此扩展方法:

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

You can use this Extension method:

public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
            where TInput : class
        {
            return (value != null) ? evaluator(value) : failureValue;
        }

Example:

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