【问题标题】:calling trygetmember on chained null references在链式空引用上调用 trygetmember
【发布时间】:2010-10-22 18:04:53
【问题描述】:

如果在路径中的任何地方遇到空引用,是否可以在链中调用 DynamicObject 实现以保持空引用结束,而不会引发任何异常?

a.b.c.e

例如:如果 a 为 null 则 a.b.c.e 为 null,或者如果 c 为 null c.e 为 null 等等?

很像 Haskell 的 Maybe monad。

【问题讨论】:

  • 看起来像 "a.b.c.e" 但行为像 "a == null ? null : a.b == null ? null : a.b.c == null ? null : a.b.c.d"?
  • @xcud: 没错,但我不想写所有这些 == ?空值:...

标签: c# dynamic null chaining


【解决方案1】:

您可以这样做,但不能针对最外层的对象,即如果a 为空,则无法访问a.b

你可以创建一个A 类的空实例,它返回它所有属性的空实例。然后a.b 将返回一个空实例B,对于c 属性将返回一个空实例C,对于e 属性将返回一个空实例E

你不会得到一个空值,但你会得到一个空实例,你可以用它来检查:

E e = a.b.c.e;
if (e != E.Empty) { ... }

如果沿途的任何属性返回一个空实例,最终结果将是E.Empty

public class A {

  public B b;

  public A(B newB) { b = newB; }

  private static A _empty = new A(B.Empty);
  public static A Empty { get { return _empty; }}

}

public class B {

  public C c;

  public B(C newC) { c = newC; }

  private static B _empty = new B(C.Empty);
  public static B Empty { get { return _empty; } }

}

public class C {

  public E e;

  public C(E newE) { e = newE; }

  private static C _empty = new C(E.Empty);
  public static C Empty { get { return _empty; } }

}

public class E {

  public string name;

  public E(string newName) { name = newName; }

  private static E _empty = new E(null);
  public static E Empty { get { return _empty; } }

}

例子:

A a1 = new A(new B(new C(new E("Hello world!"))));
A a2 = new A(new B(new C(E.Empty)));
A a3 = new A(B.Empty);

E e1 = a1.b.c.e; // e1.name returns "Hello world!"
E e2 = a2.b.c.e; // e2 == E.Empty
E e3 = a3.b.c.e; // e3 == E.Empty

【讨论】:

  • 感谢您非常详细的回答!
【解决方案2】:

查看这篇精彩的文章:Chained null checks and the Maybe monad

很多程序员都遇到过这样一种情况,即在访问嵌套对象属性(例如,person.Address.PostCode)时,他们必须进行多次空值检查。这个要求经常出现在 XML 解析中,当您尝试访问它们时,缺少的元素和属性可能会返回 null(随后尝试访问 Value 会引发 NullReferenceException)。在本文中,我将展示如何使用 C# 中的 Maybe monad 以及使用扩展方法来提高可读性。

【讨论】:

    【解决方案3】:

    这是一个穷人的安全导航扩展方法,它只是将一个表达式包装在一个 try catch 中以寻找一个 nullref。

    https://gist.github.com/1030887

    public static class Extensions
    {
        public static TResult SafeInvoke<TModel, TResult>(this TModel model, Func<TModel, TResult> expression, TResult nullValue = default(TResult))
        {
            try
            {
                return expression(model);
            }
            catch (NullReferenceException)
            {
                return nullValue;
            }
        }
    }
    

    您可以相当轻松地调用代码。

    public class MyModel
    {
      public Name Name { get; set; }
    }
    
    public class Name
    {
      public string First { get; set; }
      public string Last { get; set; }
    }
    
    var model = new MyModel();
    var firstName = model.SafeInvoke(x => x.Name.First, "john");
    var lastName = model.SafeInvoke(x => x.Name.Last, "doe");
    
    Console.WriteLine("{0}, {1}", lastName, firstName)
    // prints: "doe, john"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多