58

这里的基本问题 - 我有很多看起来像这样的代码行:

var a = (long_expression == null) ? null : long_expression.Method();

类似的行在这个函数中重复了很多。long_expression每次都不一样。我试图找到一种避免重复long_expression但保持紧凑的方法。类似的东西operator ??。目前,我正在考虑放弃并将其放在多行上,例如:

var temp = long_expression;
var a = (temp == null) ? null : temp.Method();

但是我很好奇是否有一些我不知道的聪明语法会使这更简洁。

4

7 回答 7

77

Well, you could use an extension method like this:

public static TResult NullOr<TSource, TResult>(this TSource source,
    Func<TSource, TResult> func) where TSource : class where TResult : class
{
    return source == null ? null : func(source);
}

Then:

var a = some_long_expression.NullOr(x => x.Method());

Or (depending on your version of C#)

var a = some_long_expression.NullOr(Foo.Method);

where Foo is the type of some_long_expression.

I don't think I would do this though. I'd just use the two line version. It's simpler and less clever - and while "clever" is fun for Stack Overflow, it's not usually a good idea for real code.

于 2012-06-29T19:09:11.363 回答
49

我发现这个答案很有见地。

通过执行此分配,您将 null 传播到系统的更深处。您必须再次(一次又一次地)编写您的空处理条件来处理这些传播。

与其允许使用 null 继续执行,不如将 null 替换为更好的表示形式(引用自链接)

  1. 如果 null 表示空集合,则使用空集合。
  2. 如果 null 表示异常情况,则抛出异常。
  3. 如果 null 表示意外未初始化的值,则显式初始化它。
  4. 如果 null 表示合法值,请对其进行测试 - 或者更好地使用执行 null 操作的 NullObject。

特别是,用空集合替换空集合引用为我节省了许多空测试。

于 2012-06-29T19:27:22.570 回答
5
var x = "";

/* try null instead */
string long_expression = "foo";

var a = ((x = long_expression) == null) ? null : x.ToUpper();

/* writes "FOO" (or nothing) followed by newline */
Console.WriteLine(a);

的初始化值的x类型必须与long_expression(这里:string)的类型兼容。适用于Mono C# 编译器,版本 2.10.8.1(来自 Debian 包 mono-gmcs=2.10.8.1-4)。

于 2012-06-30T07:02:46.640 回答
5

我认为 C# 语言真的可以为这种逻辑使用一个新的运算符——这很常见,一个运算符可以简化无数行代码。

我们需要类似“??”的东西。或 "if null then" 运算符,但它作为特殊的 '.' 工作。带有“if not null”条件的点运算符。为了第一 '?' 就像 "?:" if-then 的 'if' 部分和第二个 '?' 表示“null”,如可空类型。我认为我们需要一个“.?” 运算符的工作方式与“.”类似,但如果左表达式为 null 而不是抛出异常,则它只是简单地计算为 null。我想这将是一个“dot not null”运算符(好吧,所以也许“.!?”会更合乎逻辑,但我们不要去那里)。

因此,您非常常见的示例可能会丢失这样的冗余符号名称:

var a = long_expression.?Method();

有大量的 C# 代码带有嵌套的空检查,这将大大受益。想想如果这样会多好:

if (localObject != null)
{
    if (localObject.ChildObject != null)
    {
        if (localObject.ChildObject.ChildObject != null)
            localObject.ChildObject.ChildObject.DoSomething();
    }
}

可能会变成:

localObject.?ChildObject.?ChildObject.?DoSomething();

还有大量代码缺少应该存在的空检查,从而导致偶尔的运行时错误。所以,实际上使用新的'.?' 默认情况下,运算符将消除此类问题...不幸的是,我们无法逆转 '.' 的行为。和 '。?' 在这一点上,所以只有新的 '.?' 如果左侧为空,则抛出异常 - 这将是更清洁和更合乎逻辑的方式,但破坏性更改非常糟糕。虽然,有人可能会争辩说,这种特殊的变化可能会解决很多隐藏的问题,并且不太可能破坏任何东西(只有期望抛出 null ref 异常的代码)。哦,好吧……一个人总能做梦……

检查空值的唯一缺点实际上是对性能的轻微影响,这就是为什么 '.' 的原因。不检查空值。但是,我真的认为只使用 '.?' 的能力 当您关心性能时(并且知道左侧永远不会为空)会是更好的选择。

在类似的说明中,让“foreach”检查并忽略空集合也会好得多。不再需要用“if (collection != null)”来包装大多数“foreach”语句,或者更糟的是,养成始终使用空集合而不是 null 的习惯……这在某些情况下很好,但更糟糕的是在大多数集合为空的复杂对象树中执行此操作时,与空值检查相比,性能问题是空的(我见过很多)。我认为在大多数情况下,不让“foreach”检查 null 以提供更好的性能的良好意图适得其反。

Anders,做出这样的改变永远不会太晚,并添加一个编译器开关来启用它们!

于 2012-07-03T19:30:16.330 回答
1

您可以为 long_expression 评估为的任何类型编写扩展方法:

public static object DoMethod(this MyType pLongExpression)
{
   return pLongExpression == null ? null : pLongExpression.Method();
}

这将可以在任何MyType引用上调用,即使该引用是null.

于 2012-06-29T19:11:47.447 回答
0

您可以放入long_expression == null一个具有短名称的函数,然后每次都调用该函数。

于 2012-06-29T19:07:10.357 回答
0

if (!String.IsNullOrEmpty(x)) x.func()

于 2016-05-11T11:56:41.243 回答