![](/img/trans.png)
[英]Apply Linq Func<T, TResult> key selector at single element level
[英]In LINQ, how do I modify an existing LINQ extension method to add a “By” selector, i.e. add Func<T> TResult to the function signature?
我很想知道如何修改现有的LINQ函数以将Func<T> TResult
添加到函数签名中,即允许它使用选择器(o => o.CustomField)
。
例如,在C#中,我可以使用.IsDistinct()
来检查整数列表是否不同。 我也可以使用.IsDistinctBy(o => o.SomeField)
来检查字段o.SomeField
中的整数o.SomeField
是不同的。 我相信,在幕后, .IsDistinctBy(...)
有类似函数签名Func<T> TResult
附加到它?
我的问题是:获取现有LINQ扩展函数并将其转换为具有参数(o => o.SomeField)
什么?
这是一个例子。
此扩展函数检查列表是否单调增加(即值永远不会减少,如1,1,2,3,4,5,5):
main()
{
var MyList = new List<int>() {1,1,2,3,4,5,5};
DebugAssert(MyList.MyIsIncreasingMonotonically() == true);
}
public static bool MyIsIncreasingMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
如果我想添加一个“By”,我添加一个参数Func<T> TResult
。 但是如何修改函数体以使其选择(o => o.SomeField)
?
main()
{
DebugAssert(MyList.MyIsIncreasingMonotonicallyBy(o => o.CustomField) == true);
}
public static bool MyIsIncreasingMonotonicallyBy<T>(this List<T> list, Func<T> TResult) where T : IComparable
{
// Question: How do I modify this function to make it
// select by o => o.CustomField?
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
考虑如下所示的实现,它只列举给定的IEnumerable<T>
一次。 枚举可能会产生副作用,如果可能的话,调用者通常会期望单个传递。
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;
}
}
你描述的enumerable.IsIncreasingMonotonicallyBy(x => x.MyProperty)
重载现在可以写成如下。
public static bool IsIncreasingMonotonicallyBy<T, TKey>(
this IEnumerable<T> _this,
Func<T, TKey> keySelector)
where TKey : IComparable<TKey>
{
return _this.Select(keySelector).IsIncreasingMonotonically();
}
只需将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);
}
上面的一个接近右边但有问题:
您的列表可能有多个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); }
PS我相信你需要删除“this”,因为Extension方法只能在非泛型的非嵌套静态类中声明。
回应FrédéricHamidi:
考虑以下:
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 + " ");
假设GetNames()返回一个IEnumerable,我们实际上是通过在两个foreach语句中枚举这个集合两次来做额外的工作。 如果GetNames()导致数据库查询,则最终执行该查询两次,同时两次获取相同的数据。
这种问题很容易解决 - 只需通过将序列转换为列表(或者你可以做数组)来强制枚举在变量初始化点。 数组和列表类型都实现IEnumerable接口。
// the existing LINQ operator that you want to modify:
public static … Foo<T>(this IEnumerable<T> xs, …)
{
…
}
// the extended ("modified") operator:
public static … FooBy<T, U>(this IEnumerable<T> xs, Func<T, U> func)
{
return xs.Select(func).Foo(…);
} // --^^^^^^^^^^^^^-------
// there you go!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.