2010-10-22 47 views
0

是否有可能在链中调用动态对象实现,如果遇到空引用的路径中的任何位置,而不抛出任何异常,则保持空引用结束?在链式空引用上调用trygetmember

a.b.c.e 

例如:如果是空值,则a.b.c.e为空,或者如果c为null C.E为null等?

非常喜欢哈斯克尔的Maybe monad。

+0

看起来像“a.b.c.e”但行为像“a == null?null:a.b == null?null:a.b.c == null?null:a.b.c.d”? – xcud 2010-10-22 18:09:28

+0

@xcud:是的,但我不想写所有这些==? nulls:... – 2010-10-22 18:10:21

回答

1

您可以做类似的事情,但不是最外层的对象,即如果a为空,则无法访问a.b

您可以创建A类的空实例,该实例返回所有属性的空实例。然后a.b将返回一个B的空实例,对于c属性将返回一个空实例Ce属性将返回一个空实例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 
+0

感谢您提供非常详细的答案! – 2010-10-22 18:42:42

1

检查这篇大文章:Chained null checks and the Maybe monad

一个伟大的很多程序员都满足的情况下,在访问一个嵌套的对象属性(例如,person.Address .PostCode),他们必须做几次空的检查。这个需求经常在XML解析中弹出,当你尝试访问它们(并且随后尝试访问Value时抛出一个NullReferenceException)时,缺失的元素和属性可能返回null。在本文中,我将介绍如何使用C#中的Maybe monad,以及使用扩展方法来提高可读性。

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"