注意:这个问题是在引入the .?
operator in C# 6 / Visual Studio 2015之前提出的。
我们都遇到过这样的情况,我们有一些像cake.frosting.berries.loader这样的深层属性,我们需要检查它是否为空,所以没有异常。这样做的方法是使用短路if语句
if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...
这并不是很优雅,也许应该有一种更简单的方法来检查整个链,看看它是否会遇到null变量/属性。
是否可以使用某种扩展方法,或者这将是一种语言功能,或者这只是一个糟糕的想法?
发布于 2010-01-18 00:38:46
我们已经考虑添加一个新的操作“?”具有您想要的语义的语言。(现在已经添加了它;请参见下面的内容。)也就是说,你会说
cake?.frosting?.berries?.loader
编译器会为你生成所有的短路检查。
它并没有成为C# 4的标准,也许是作为该语言假想的未来版本。
更新(2014): ?.
运算符现在是下一个编译器版本的planned。请注意,对于操作符的确切语法和语义分析,仍然存在一些争论。
更新( 2015年7月): Visual Studio2015已经发布,并附带了支持null-conditional operators ?.
and ?[]
的C#编译器。
发布于 2010-01-18 01:41:30
我受到这个问题的启发,试图找出如何通过使用表达式树的更简单/更漂亮的语法来完成这种深度空检查。虽然我确实同意这样的回答,即如果您经常需要访问层次结构中的深层实例,那么这可能是一个糟糕的设计,但我也确实认为在某些情况下,例如数据表示,它可能非常有用。
所以我创建了一个扩展方法,它允许你写:
var berries = cake.IfNotNull(c => c.Frosting.Berries);
如果表达式没有任何部分为空,这将返回浆果。如果遇到null,则返回null。不过有一些需要注意的地方,在当前版本中,它只适用于简单的成员访问,而且它只适用于.NET Framework4,因为它使用了v4中的新方法MemberExpression.Update。下面是IfNotNull扩展方法的代码:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace dr.IfNotNullOperator.PoC
{
public static class ObjectExtensions
{
public static TResult IfNotNull<TArg,TResult>(this TArg arg, Expression<Func<TArg,TResult>> expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
if (ReferenceEquals(arg, null))
return default(TResult);
var stack = new Stack<MemberExpression>();
var expr = expression.Body as MemberExpression;
while(expr != null)
{
stack.Push(expr);
expr = expr.Expression as MemberExpression;
}
if (stack.Count == 0 || !(stack.Peek().Expression is ParameterExpression))
throw new ApplicationException(String.Format("The expression '{0}' contains unsupported constructs.",
expression));
object a = arg;
while(stack.Count > 0)
{
expr = stack.Pop();
var p = expr.Expression as ParameterExpression;
if (p == null)
{
p = Expression.Parameter(a.GetType(), "x");
expr = expr.Update(p);
}
var lambda = Expression.Lambda(expr, p);
Delegate t = lambda.Compile();
a = t.DynamicInvoke(a);
if (ReferenceEquals(a, null))
return default(TResult);
}
return (TResult)a;
}
}
}
它的工作方式是检查表示表达式的表达式树,一个接一个地计算各个部分;每次检查结果是否不为空。
我相信这是可以扩展的,这样就可以支持MemberExpression以外的其他表达式。
发布于 2010-01-17 19:06:44
我发现这个扩展对于深度嵌套的场景非常有用。
public static R Coal<T, R>(this T obj, Func<T, R> f)
where T : class
{
return obj != null ? f(obj) : default(R);
}
这是我从C#和T-SQL中的null合并运算符衍生而来的想法。好的是返回类型总是内部属性的返回类型。
这样你就可以这样做了:
var berries = cake.Coal(x => x.frosting).Coal(x => x.berries);
...or是上述内容的一个细微变化:
var berries = cake.Coal(x => x.frosting, x => x.berries);
这不是我所知道的最好的语法,但它确实起作用了。
https://stackoverflow.com/questions/2080647
复制相似问题