首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >深度空检查,有没有更好的方法?

深度空检查,有没有更好的方法?
EN

Stack Overflow用户
提问于 2010-01-17 18:17:40
回答 16查看 24.1K关注 0票数 133

注意:这个问题是在引入the .? operator in C# 6 / Visual Studio 2015之前提出的。

我们都遇到过这样的情况,我们有一些像cake.frosting.berries.loader这样的深层属性,我们需要检查它是否为空,所以没有异常。这样做的方法是使用短路if语句

代码语言:javascript
复制
if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...

这并不是很优雅,也许应该有一种更简单的方法来检查整个链,看看它是否会遇到null变量/属性。

是否可以使用某种扩展方法,或者这将是一种语言功能,或者这只是一个糟糕的想法?

EN

回答 16

Stack Overflow用户

回答已采纳

发布于 2010-01-18 00:38:46

我们已经考虑添加一个新的操作“?”具有您想要的语义的语言。(现在已经添加了它;请参见下面的内容。)也就是说,你会说

代码语言:javascript
复制
cake?.frosting?.berries?.loader

编译器会为你生成所有的短路检查。

它并没有成为C# 4的标准,也许是作为该语言假想的未来版本。

更新(2014): ?.运算符现在是下一个编译器版本的planned。请注意,对于操作符的确切语法和语义分析,仍然存在一些争论。

更新( 2015年7月): Visual Studio2015已经发布,并附带了支持null-conditional operators ?. and ?[]的C#编译器。

票数 225
EN

Stack Overflow用户

发布于 2010-01-18 01:41:30

我受到这个问题的启发,试图找出如何通过使用表达式树的更简单/更漂亮的语法来完成这种深度空检查。虽然我确实同意这样的回答,即如果您经常需要访问层次结构中的深层实例,那么这可能是一个糟糕的设计,但我也确实认为在某些情况下,例如数据表示,它可能非常有用。

所以我创建了一个扩展方法,它允许你写:

代码语言:javascript
复制
var berries = cake.IfNotNull(c => c.Frosting.Berries);

如果表达式没有任何部分为空,这将返回浆果。如果遇到null,则返回null。不过有一些需要注意的地方,在当前版本中,它只适用于简单的成员访问,而且它只适用于.NET Framework4,因为它使用了v4中的新方法MemberExpression.Update。下面是IfNotNull扩展方法的代码:

代码语言:javascript
复制
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以外的其他表达式。

票数 27
EN

Stack Overflow用户

发布于 2010-01-17 19:06:44

我发现这个扩展对于深度嵌套的场景非常有用。

代码语言:javascript
复制
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合并运算符衍生而来的想法。好的是返回类型总是内部属性的返回类型。

这样你就可以这样做了:

代码语言:javascript
复制
var berries = cake.Coal(x => x.frosting).Coal(x => x.berries);

...or是上述内容的一个细微变化:

代码语言:javascript
复制
var berries = cake.Coal(x => x.frosting, x => x.berries);

这不是我所知道的最好的语法,但它确实起作用了。

票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2080647

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档