You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LINQ中自定义Select()扩展方法的正确使用方式

问题解答

一、数组转Enumerable再转回数组是否属于代码坏味道?

首先,AsEnumerable()并没有创建新的集合对象——数组本身就实现了IEnumerable<S>,这个方法只是改变了变量的静态类型,从S[]变为IEnumerable<S>,性能上几乎没有额外开销,所以单纯这一步不算代码坏味道。

但你的实现本质上还是依赖原生LINQ的Select再转数组,和直接写array.Select(...).ToArray()没有本质区别,只是做了一层封装。如果想真正避免中间枚举对象的开销,更优的方式是直接遍历数组生成新数组,跳过LINQ的中间迭代器:

static class EnumerableExtensions
{
    public static R[] Select<S, R>(this S[] self, Func<S, R> func)
    {
        var result = new R[self.Length];
        for (int i = 0; i < self.Length; i++)
        {
            result[i] = func(self[i]);
        }
        return result;
    }
}

这种实现完全没有中间枚举对象,效率更高,也更贴合你“直接生成数组”的初衷。

二、如何让C#明确调用不同版本的Select?

1. 调用自定义版本(返回数组)

当直接在S[]上调用Select时,因为自定义扩展方法的参数更具体(S[]比IEnumerable<S>匹配度更高),C#会优先选择你的自定义版本:

string[] thisIsArray = array.Select(a => a.Property); // 调用自定义版本
var whatIsThis = array.Select(a => a.Property); // 推断类型为string[],调用自定义版本

2. 调用原生LINQ版本(返回IEnumerable<string>)

如果想强制调用原生的Select,需要先把数组的静态类型转为IEnumerable<S>,引导编译器匹配原生扩展方法:

// 显式声明类型时,先转IEnumerable再调用
IEnumerable<string> thisIsEnumerable = array.AsEnumerable().Select(a => a.Property);

// 使用var时,同样先转类型来改变推断结果
var whatIsThis = array.AsEnumerable().Select(a => a.Property); // 推断类型为IEnumerable<string>

需要注意:如果直接写IEnumerable<string> thisIsEnumerable = array.Select(...),C#还是会调用自定义版本——因为string[]可以隐式转换为IEnumerable<string>,编译器依然会优先选择参数更具体的自定义方法,必须通过AsEnumerable()切换静态类型才能触发原生版本的调用。

内容的提问来源于stack exchange,提问作者Konrad Viltersten

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:47:21