C# 在LINQ中,如何修改现有的LINQ扩展方法以添加一个;由「;选择器,即添加函数<;T>;t函数签名的结果?

C# 在LINQ中,如何修改现有的LINQ扩展方法以添加一个;由「;选择器,即添加函数<;T>;t函数签名的结果?,c#,.net,linq,C#,.net,Linq,我很想知道如何修改现有的LINQ函数,将Func TResult添加到函数签名中,即允许它使用(o=>o.CustomField)中的选择器 例如,在C#中,我可以使用.IsDistinct()检查整数列表是否不同。我还可以使用.IsDistinctBy(o=>o.SomeField)检查fieldo.SomeField中的整数是否不同。我相信,在幕后,.IsDistinctBy(…)有类似于函数签名Func TResult的附加内容 我的问题是:获取现有LINQ扩展函数并将其转换为具有参数(o

我很想知道如何修改现有的LINQ函数,将
Func TResult
添加到函数签名中,即允许它使用
(o=>o.CustomField)
中的选择器

例如,在C#中,我可以使用
.IsDistinct()
检查整数列表是否不同。我还可以使用
.IsDistinctBy(o=>o.SomeField)
检查field
o.SomeField
中的整数是否不同。我相信,在幕后,
.IsDistinctBy(…)
有类似于函数签名
Func TResult
的附加内容

我的问题是:获取现有LINQ扩展函数并将其转换为具有参数
(o=>o.SomeField)
的技术是什么

这里有一个例子

此扩展函数检查列表是否单调递增(即值从不递减,如1,1,2,3,4,5,5所示):


只需将
Func
应用于a和b:

public static bool MyIsIncreasingMonotonicallyBy<T, TResult>(this IEnumerable<T> list, Func<T, TResult> selector)
    where TResult : IComparable<TResult>
{
    return list.Zip(list.Skip(1), (a, b) => selector(a).CompareTo(selector(b)) <= 0).All(b => b);
}
public static bool myIsIncreasing单调递增(此IEnumerable列表,Func选择器)
结果:i可比较
{
return list.Zip(list.Skip(1)、(a,b)=>selector(a)、CompareTo(selector(b))b);
}

上面的一个靠近右侧,但存在以下问题:

  • 您的列表可能有多个IEnumeration枚举

    public static bool MyIsIncreasingMonotonicallyBy<T, TResult>(
            this IEnumerable<T> list, Func<T, TResult> selector)
        where TResult : IComparable<TResult>
    {
        var enumerable = list as IList<T> ?? list.ToList();
        return enumerable.Zip(
                   enumerable.Skip(1),
                   (a, b) => selector(a).CompareTo(selector(b)) <= 0
               ).All(b => b);
    }
    
    公共静态bool myis单调递增(
    此IEnumerable列表(函数选择器)
    结果:i可比较
    {
    var enumerable=列表为IList??list.ToList();
    返回可枚举的.Zip(
    可枚举。跳过(1),
    (a,b)=>选择器(a)。与(选择器(b))b比较;
    }
    
  • 另外,我认为您需要删除“this”,因为扩展方法只能在非泛型、非嵌套的静态类中声明


    针对弗里德里克·哈米迪:

    考虑以下几点:

    IEnumerable<string> names = GetNames();
    foreach (var name in names)   Console.WriteLine("Found " + name);
    var allNames = new StringBuilder();
    foreach (var name in names)   allNames.Append(name + " ");
    
    IEnumerable name=GetNames();
    foreach(名称中的变量名)Console.WriteLine(“找到”+名称);
    var allNames=new StringBuilder();
    foreach(名称中的变量名)allNames.Append(name+“”);
    
    假设GetNames()返回一个IEnumerable,那么通过在两个foreach语句中枚举此集合两次,我们实际上是在做额外的工作。如果GetNames()生成一个数据库查询,那么您将执行两次该查询,同时两次都获得相同的数据


    这种问题很容易解决——只需通过将序列转换为列表(或者执行数组),在变量初始化时强制枚举即可。数组和列表类型都实现了IEnumerable接口。

    考虑如下实现,它只枚举给定的
    IEnumerable
    一次。枚举可能会产生副作用,如果可能的话,调用方通常期望单次通过

    public static bool IsIncreasingMonotonically<T>(
        this IEnumerable<T> _this)
        where T : IComparable<T>
    {
        using (var e = _this.GetEnumerator())
        {
            if (!e.MoveNext())
                return true;
            T prev = e.Current;
            while (e.MoveNext())
            {
                if (prev.CompareTo(e.Current) > 0)
                    return false;
                prev = e.Current;
            }
            return true;
        }
    }
    

    您是否尝试在比较操作中将
    x()
    应用于
    a
    b
    ?另外,
    x
    不应该是一个
    Func
    ,并且
    TResult
    被限制为
    IComparable
    ?我建议不要这样做。您的函数声称支持
    IEnumerable
    ,但如果它不是列表,则会耗尽其参数。像这样的穷尽枚举可能代价高昂,甚至永远不会返回。如果您只能支持列表,那么使用
    List
    (或
    IList
    )参数确实是正确的做法;什么使您相信它不在非泛型非嵌套静态类中?还要注意的是,由于他的版本中的参数是
    List
    ,因此多次枚举它没有问题。如果将
    IEnumerable
    作为参数,则只有多次枚举才有问题。也可以使用IEnumerable,执行此方法,但仍然保持延迟执行,这在这么少的代码中是不可能做到的。您的答案非常好。如果你还把你的答案的第一部分加在我前面的一个问题上,那么我会投上一票,看。@Gravitas是的,基本上可以归结为,能够使用任何序列,而不仅仅是一个列表,所增加的通用性是否值得该方法增加的复杂性。如果这是您想要的,那么这是更好的实现。如果你不需要它,作为一个开发人员,它可能更难理解和维护。我同意这比Thomas Levesque给出的答案更难维护。你一眼就能看出他是干什么的,但这件事更令人困惑。但是,它也有更好的“黑盒”属性。这会枚举列表两次,并对n个可枚举项调用选择器最多(2n-1)次。@TimothyShields,确实如此。我只是使用了OP的代码,没有尝试优化它。更好的方法是在循环中枚举集合,跟踪上一个值,但由于这不是OP所要求的,所以我没有费心。。。
    IEnumerable<string> names = GetNames();
    foreach (var name in names)   Console.WriteLine("Found " + name);
    var allNames = new StringBuilder();
    foreach (var name in names)   allNames.Append(name + " ");
    
    public static bool IsIncreasingMonotonically<T>(
        this IEnumerable<T> _this)
        where T : IComparable<T>
    {
        using (var e = _this.GetEnumerator())
        {
            if (!e.MoveNext())
                return true;
            T prev = e.Current;
            while (e.MoveNext())
            {
                if (prev.CompareTo(e.Current) > 0)
                    return false;
                prev = e.Current;
            }
            return true;
        }
    }
    
    public static bool IsIncreasingMonotonicallyBy<T, TKey>(
        this IEnumerable<T> _this,
        Func<T, TKey> keySelector)
        where TKey : IComparable<TKey>
    {
        return _this.Select(keySelector).IsIncreasingMonotonically();
    }