为了账号安全,请及时绑定邮箱和手机立即绑定

深度检查,是否有更好的方法?

深度检查,是否有更好的方法?

C#
波斯汪 2019-07-29 17:19:45
深度检查,是否有更好的方法?我们都去过那里,我们有像cake.frosting.berries.loader这样的深层属性,我们需要检查它是否为空,所以没有例外。要做的是使用短路if语句if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...这不是很优雅,也许应该有一种更简单的方法来检查整个链,看看它是否出现了null变量/属性。是否可以使用某种扩展方法或者它是一种语言功能,还是只是一个坏主意?
查看完整描述

3 回答

?
泛舟湖上清波郎朗

TA贡献1818条经验 获得超3个赞

我们考虑过添加新的操作“?”。到具有您想要的语义的语言。(现在已添加;见下文。)也就是说,你会说

cake?.frosting?.berries?.loader

并且编译器会为您生成所有短路检查。

它没有成为C#4的标准。也许是对于该语言的假设未来版本。

更新(): 该?.运营商正在计划在未来罗斯林编译器版本。请注意,对运算符的确切语法和语义分析仍存在争议。

更新(): Visual Studio 2015已经发布,并附带一个C#编译器,支持null条件运算符?.?[]


查看完整回答
反对 回复 2019-07-29
?
MM们

TA贡献1886条经验 获得超2个赞

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

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

var berries = cake.IfNotNull(c => c.Frosting.Berries);

如果表达式的任何部分都不为null,则返回Berries。如果遇到null,则返回null。但是有一些注意事项,在当前版本中它只能用于简单的成员访问,它只适用于.NET Framework 4,因为它使用了MemberExpression.Update方法,这是v4中的新方法。这是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之外的其他表达式。将其视为概念验证代码,请记住,使用它会导致性能下降(在许多情况下可能无关紧要,但不要在紧密循环中使用它:-))


查看完整回答
反对 回复 2019-07-29
?
潇湘沐

TA贡献1816条经验 获得超6个赞

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

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中的空合并运算符中获得的一个想法。好处是返回类型始终是内部属性的返回类型。

这样你可以这样做:

var berries = cake.Coal(x => x.frosting).Coal(x => x.berries);

...或上述的略有变化:

var berries = cake.Coal(x => x.frosting, x => x.berries);

这不是我所知道的最好的语法,但确实有效。


查看完整回答
反对 回复 2019-07-29
  • 3 回答
  • 0 关注
  • 483 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信