C#中的安全导航操作员?

时间:2010-11-05 22:34:58

标签: c#

  

可能重复:
  Shortcut for “null if object is null, or object.member if object is not null”

某些语言有一个安全的导航操作符,可以让您不必担心空引用异常。

语言示例Groovy

String lname = person.Name.ToLowerCase(); //throws exception if Name is null
String lname = person.Name?.ToLowerCase();//lname will be null if Name was null

如何在C#中完成与此类似的操作?到目前为止,我的解决方案是这样的扩展方法:

public static T o<T>(this T obj) where T : new()
{
            return obj != null ? obj : new T();
}
//used like: String lname = person.o().Name; //returns null if person was null

但是,这只适用于某些情况。

4 个答案:

答案 0 :(得分:10)

对于这种情况,我倾向于使用名为IfNotNull的扩展方法:

public static OUT IfNotNull<IN, OUT>(this IN v, Func<IN, OUT> f) 
  where IN : class where OUT : class
{
  return v == null ? null : f(v);
}

更复杂的是介绍一个Maybe的概念。 derick bailey here带来了一个例子。

<强>更新

C# 6开始,现在有一个空传播运算符,它的语法方式与Groovy运算符完全相同。

答案 1 :(得分:4)

您正在寻找在C#语言版本6中引入的短路 null-conditional member access operator ?. (在Visual Studio 2015中推出)。

我的答案的其余部分是为早期版本的C#语言编写的,该语言没有?.运算符。


一般来说,如果您正在访问一个深度“嵌套”的属性,例如outermostObject.a.b.c.X,您应该考虑重新设计代码,因为这样的访问可能表明您违反了既定的OO原则(例如最低知识原则,又名Demeter法则)。

其他一些选择:

首先,一个反建议 - 不要这样做:

string lname = null;
try
{
    lname = Person.Name.ToLower();
}
catch (NullReferenceException ex) { }  // inefficient and ugly

第二次,使用像Maybe monad这样的东西 - 您可以自己定义这样的类型。它基本上是Nullable<T>,它实现IEnumerable<T>,使得它在没有设置值时返回空序列,或者如果设置了值则返回一个恰好一个元素的序列。然后你可以按如下方式使用它:

Maybe<string> personName = person.Name;
var lname = (from name in personName select name.ToLower()).FirstOrDefault();

第三次,也许是ulrichb建议的最简单,最实用的解决方案:

var lname = person.Name != null ? person.Name.ToLower() : null;

PS ,因为我们已经讨论了检查null的主题,所以在访问之前不要忘记检查person是否为null Name属性......; - )

答案 2 :(得分:3)

我不知道从保证不为null的内容返回null,但为了保证对象引用,您可以使用Null Coalescing Operator ??

类似的东西:

string lname = (person.Name??String.Empty).ToLower();

对于null case,它将返回一个空字符串而不是null,但它会起作用。

返回空字符串比返回null更有意义;如果你返回一个null,如果你将另一个运算符链接到它上,它将再次抛出。

答案 3 :(得分:2)

今天在C#中不存在,但您可以使用SelectMany编写它。

String lname = from _ in person.Name from s in _.ToUpper() select s;

String lname = person.Name.SelectMany(_ => _.ToUpper(), s => s);

(这是Bart De Smet在his PDC 2010 talk on the future of LINQ中提出的建议。见幻灯片#6。)