![](/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.