C# 在LINQ中,如何修改现有的LINQ扩展方法以添加一个;由「;选择器,即添加函数<;T>;t函数签名的结果?
我很想知道如何修改现有的LINQ函数,将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
Func TResult
添加到函数签名中,即允许它使用(o=>o.CustomField)
中的选择器
例如,在C#中,我可以使用.IsDistinct()
检查整数列表是否不同。我还可以使用.IsDistinctBy(o=>o.SomeField)
检查fieldo.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);
}
上面的一个靠近右侧,但存在以下问题:
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比较;
}
针对弗里德里克·哈米迪: 考虑以下几点:
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();
}