在链式空引用上调用trygetmember

时间:2010-10-22 18:04:54

标签: c# dynamic null chaining

是否有可能在链中调用一个DynamicObject实现,如果遇到空引用的路径中的任何地方,则保持空引用结束,而不会抛出任何异常?

a.b.c.e

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

非常像Haskell的Maybe monad。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

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

  

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

答案 2 :(得分:1)

这是一个穷人的安全导航扩展方法,它只是在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"