我正在做一件非常简单和直截了当的事情,我不禁怀疑/希望它能变得更短.

出于示例的目的,让我们假设我有一个IEnumerable<int?>,而我想要的只是一个只产生非空值的IEnumerable<int>.以下是一些可以工作的代码,但可以让它更简洁吗?

public static void Main( string[] args )
{
    int?[] a = { null, 42, null, 5 };
    IEnumerable<int> ints = a //
            .Where( i => i.HasValue ) //
            .Select( i => i!.Value );
    foreach( var i in ints )
        Console.WriteLine( i );
    Console.ReadLine();
}

请注意,如果它能变得更简洁,那么它也会变得更整洁,因为丑陋的i!可以避免.

但也请注意,这只是一个示例,所以请不要关注这些是可以为空的整数这一事实.它们可以很容易地是一些可能被派生的复杂对象,我们正试图获得仅那些派生更多的对象的枚举,这些对象转换为更派生的类型.

那么,这个问题就是如何在一条语句中做一个Where()Select(),从而一步实现转换和过滤,从而利用过滤过程中所做的工作,减少转换过程中所需的工作量.

推荐答案

考虑使用LINQ OfType方法.如果您的值为IEnumerable<int?>,则.OfType<int>()将返回IEnumerable<int>并排除为空的元素.同样,如果您有IEnumerable<Base>,则.OfType<Derived>()返回IEnumerable<Derived>并排除其他类型的对象.

对于比按类型过滤更复杂的情况,您可以编写自己的扩展方法:

public static IEnumerable<TResult> SelectWhere<TSource, TResult>(
    this IEnumerable<TSource> source, Func<TSource, (bool, TResult)> selector) 
{
    foreach (TSource item in source)
        if (selector(item) is (true, var result))
            yield return result;
}

对于每个输入值,如果应该包括该值,则selector应该返回(true, transformedValue),如果应该排除该值,则返回(false, default).

例如,给定数组int?[] a,语句

IEnumerable<int> negatedInts = a
    .Where(i => i.HasValue)
    .Select(i => -i!.Value);

可以像这样重写:

IEnumerable<int> negatedInts = a
    .SelectWhere(i => i is int value ? (true, -value) : (false, default));

Csharp相关问答推荐

LINQ无法翻译SQLFunctions方法

如何在C#中删除一个特殊字符,如"使用Regex"

如何从HttpContext获取请求正文

MAUI查询参数单一字符串项将不起作用

Blazor EventCallback<;MyType<;T>;>;

Unix上的.NET(核心):.NET意外地未看到通过P/Invoke系统调用对环境变量进行的进程内修改

在swagger示例中添加默认数组列表

显示文档的ECDsa签名PDF在Adobe Reader中签名后已被更改或损坏

如何在C#中转换泛型包装类内部的派生类

如何将MemberInitExpression添加到绑定中其他Lambda MemberInitExpression

升级后发出SWITCH语句

.NET 8在appsettings.json中核心使用词典URI、URI&>

使用ASP.NET MVC for Lemon Squeezy X-Signature创建散列

实体框架-IsRequired()与OnDelete()

如何强制新设置在Unity中工作?

具有嵌套属性的IGGroup

如何使用.NET 8.0中新的CompositeFormat类?

为什么我的属性即使没有显式地设置任何[必需]属性,也会显示验证?

读取测试项目中的应用程序设置

当我在Git中暂存文件更改时,它们会消失