【问题标题】:C# LINQ: remove null values from an array and return as not nullableC# LINQ:从数组中删除空值并返回不可为空
【发布时间】:2021-08-30 15:15:28
【问题描述】:

我正在将可空数组转换为不可空数组。这是我当前的代码,有两个函数调用:

myarray.Where(e => e.HasValue).Select(e => e.Value)

这看起来是一个非常基本的操作。是否可以在一个电话中做到这一点?

【问题讨论】:

  • 我认为这是你能做的最好的。还是很简短的表达。
  • 不,如果您想删除所有空值(步骤 1),然后将结果转换为基础类型,其中 Nullable<T> 被强制转换回 T(步骤 2),这是很好。

标签: c# linq


【解决方案1】:
myarray.OfType<int>();

这是可行的,因为可空类型如果不为空,则绑定到其基础类型,但如果它们为空,则不绑定。

编辑:我要指出的唯一一点是,我制作的单线的语义与您的略有不同。可能是你想要你的而不是我的。您的代码:“获取所有具有值的可为空对象”。我的代码:“获取所有可以成功转换为 int 的类型”

【讨论】:

  • 完美!这就是我一直在寻找的。​​span>
  • 我要指出的唯一一点是,我制作的单线的语义与您的略有不同。可能是你想要你的而不是我的。您的代码:“获取所有具有值的可为空对象”。我的代码:“抓取所有可以成功转换为int的类型”。
  • @DmitryPetrov 此代码涉及对数组的每个元素进行装箱、类型检查和拆箱操作,因此与您的原始代码相比效率非常低。永远不要在生产中使用它。你的代码很好。
  • 就其价值而言,可空性跟踪 (C# 8/.NET Core 3.1) 仍然存在粗糙的边缘,这种方法避免了可空性警告,而其他方法则没有。在性能不重要的地方,很高兴知道。参考:github.com/dotnet/roslyn/issues/37468#issuecomment-515142288
【解决方案2】:

你总是可以自己做扩展,但是只会让你的代码看起来更简洁,认为你的实现是你能得到的最简洁明了的说实话

public static IEnumerable<T> GetNonNullValues<T>(this IEnumerable<Nullable<T>> items) where T: struct
{
    return items.Where(a=>a.HasValue).Select(a=>a.Value);
}

【讨论】:

    【解决方案3】:

    尝试使用.OfType(...)

    例子……

    myarray.OfType<int>()
    

    ...这对我有用...

    var d = new int?[] { 1, 2, 3, 4, 5, null, null, 6, 7, 8, 9, null };
    Console.WriteLine(d.Count()); // 12
    Console.WriteLine(d.OfType<int>().Count()); //9
    

    【讨论】:

    • 很好的解决方案!我接受约翰的评论只是因为他是第一个。
    • 排名第一,但我在他之前回答;)
    【解决方案4】:

    试试这个:

    myarray.Where(e => e.HasValue).Select(e => e.GetValueOrDefault()))
    

    【讨论】:

    • 这仍然是 2 个步骤,我看不出这比问题中的优势。
    • 这实际上比 OPs 版本的效率而且更长。
    • 好吧,你是对的。如果你想排除空项目,我想我同意 Eli 是最短的方法。
    【解决方案5】:

    您的表达式尽可能漂亮,并且由于 Enumerables 的循环方式,您的集合在技术上不会在两个单独的循环中被访问两次。

    如果你仍然想用一个表达式来做,这里是许多丑陋的方法之一。

    myarray.Aggregate(Enumerable.Empty<YourElementType>(), (prev, next) => next.HasValue? prev.Concat(new [] { next }) : prev);
    

    绝对不能使用以上内容,仅用于演示目的。我会使用你在问题中写的,你的更漂亮。

    【讨论】:

      【解决方案6】:

      虽然.OfType&lt;&gt; 解决方案有效,但那里的意图还不是很清楚,我建议这个解决方案与问题中的解决方案几乎相同,但它允许编译器和例如R# 以确保在输出类型中并且没有针对可能的空引用异常的警告(例如 C# 8 中的可为空引用类型)。

      myarray
        .Where(e => e != null)
        .Select(e => e ?? new Exception("Unexpected null value"))
      

      这似乎有点矫枉过正,可以通过例如“e!”,但它对未来的错误更安全(例如,如果错误地修改了 where 子句)

      【讨论】:

      • IMO 两次检查 null 并抛出一个仔细观察永远不会发生的异常的意图也不清楚。抛出异常会使代码看起来非常危险并且非常分散注意力。我不会纯粹为了让 R# 开心而构建代码。然后至少使用GetValueOrDefault(),即使“默认”部分永远不会被击中。
      • 它不会让 R# 开心,它会让编译器开心,这是一个很大的不同。不幸的是,可空性推断不够聪明,无法确定 Select 中不能有任何非空实体。此外,将来可以更改 Where 子句,而无需有人了解后果,然后将触发异常。
      • 异常是多余的。此外,它不会编译。我似乎无法找到 throw 表达式可以与 null 合并运算符一起使用的确切情况,但不支持这种情况。我只能在评估value 参数的属性设置器中在 C# 8 中编译它。
      • 我不确定你需要哪个版本的 .net/C# 来编译它,但是最新的 dotnet5/c# 9 编译就好了。这是最近引入的一种新语法。可枚举的类型应该可以为空,并且您必须启用可空性。
      • 另外GetValueOrDefault 将不起作用,因为它不会“删除”可空性。也许答案不是很清楚,但是如果不开启可为空的引用类型,它可能没有那么有用,但是当您打开它时,使用其他解决方案是不够的。
      猜你喜欢
      • 2022-08-09
      • 2017-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-23
      相关资源
      最近更新 更多