使用扩展方法,我们可以编写方便的LINQ运算符来解决通用问题。
我想听听System.Linq
命名空间中缺少哪些方法或重载以及如何实现它们。
最好使用可能使用现有方法的简洁明了的实现。
使用扩展方法,我们可以编写方便的LINQ运算符来解决通用问题。
我想听听System.Linq
命名空间中缺少哪些方法或重载以及如何实现它们。
最好使用可能使用现有方法的简洁明了的实现。
Answers:
(自编写此答案以来,这些内容已添加到.NET。)
/// <summary>Adds a single element to the end of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing all the input elements, followed by the
/// specified additional element.</returns>
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T element)
{
if (source == null)
throw new ArgumentNullException("source");
return concatIterator(element, source, false);
}
/// <summary>Adds a single element to the start of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing the specified additional element, followed by
/// all the input elements.</returns>
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head)
{
if (tail == null)
throw new ArgumentNullException("tail");
return concatIterator(head, tail, true);
}
private static IEnumerable<T> concatIterator<T>(T extraElement,
IEnumerable<T> source, bool insertAtStart)
{
if (insertAtStart)
yield return extraElement;
foreach (var e in source)
yield return e;
if (!insertAtStart)
yield return extraElement;
}
return source.Concat(Enumerable.Repeat(element, 1));
。
head.AsEnumerable().Concat(source)
/source.Concat(element.AsEnumerable())
令我惊讶的是,还没有人提到MoreLINQ项目。它由乔恩·斯凯特(Jon Skeet)创立,并在此过程中吸引了一些开发人员。从项目页面:
LINQ to Objects缺少一些理想的功能。
该项目将以额外的方法增强LINQ to Objects,并保持LINQ的精神。
查看Operators Overview Wiki页面,了解已实施的运营商的列表。
当然,这是学习一些简洁优雅的源代码的好方法。
纯粹主义者什么都没有,但是该死的很有用!
public static void Each<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var i in items)
action(i);
}
Select
,是内置的。–
/// <summary>Creates a <see cref="Queue<T>"/> from an enumerable
/// collection.</summary>
public static Queue<T> ToQueue<T>(this IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
return new Queue<T>(source);
}
/// <summary>Creates a <see cref="Stack<T>"/> from an enumerable
/// collection.</summary>
public static Stack<T> ToStack<T>(this IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
return new Stack<T>(source);
}
var myQueue = new Queue<ObjectType>(myObj);
?对于仅一行来说,这并不是真正值得的扩展……
ToList()
并且这些扩展也ToArray()
很好地补充了扩展。var myQueue = a.SelectMany(...).Where(...).OrderBy(...).ToQueue()
与更传统的语法相比,我更喜欢流利的语言。
<ObjectType>
如果编译器可以推断出它,我不想在那儿写。
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
return !source.Any();
}
None
Any
比更加类似于IsEmpty
。
其他两个著名的SQL构造的C#等效项
/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, params T[] values)
{
if (values == null)
return false;
if (values.Contains<T>(value))
return true;
return false;
}
/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, IEnumerable<T> values)
{
if (values == null)
return false;
if (values.Contains<T>(value))
return true;
return false;
}
/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, params T[] values)
{
return In(value, values) == false;
}
/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, IEnumerable<T> values)
{
return In(value, values) == false;
}
if (values == null) return false;
我认为应该抛出异常。悄无声息地吞并错误情况永远都不是好事。
return values.Contains(value);
是不够的。
/// <summary>
/// Returns a sequence containing one element.
/// </summary>
public static IEnumerable<T> AsIEnumerable<T>(this T obj)
{
yield return obj;
}
用法:
var nums = new[] {12, 20, 6};
var numsWith5Prepended = 5.AsIEnumerable().Concat(nums);
Append
和Prepend
为此。
return new T[] { obj };
使用。这样,编译器不必为了产生一个值而构造整个状态机类。
new[]{1, 2, 3, 4}.AsIEnumerable()
什么?我期望1,2,3,4,而不是[1,2,3,4]。
EnumerableEx.Return(new[]{1, 2, 3, 4})
。没错,“ As”表示正在进行一些强制转换,并且由于数组已经实现了IEnumerable,因此您期望什么都不会改变。
与基本上相同string.Join
,但:
可以在任何集合上使用它,而不仅仅是字符串集合(ToString
在每个元素上调用)
能够为每个字符串添加前缀和后缀。
作为扩展方法。我觉得string.Join
很烦,因为它是静态的,这意味着在一系列操作中,它在词法上不正确。
/// <summary>
/// Turns all elements in the enumerable to strings and joins them using the
/// specified string as the separator and the specified prefix and suffix for
/// each string.
/// <example>
/// <code>
/// var a = (new[] { "Paris", "London", "Tokyo" }).JoinString(", ", "[", "]");
/// // a contains "[Paris], [London], [Tokyo]"
/// </code>
/// </example>
/// </summary>
public static string JoinString<T>(this IEnumerable<T> values,
string separator = null, string prefix = null, string suffix = null)
{
if (values == null)
throw new ArgumentNullException("values");
using (var enumerator = values.GetEnumerator())
{
if (!enumerator.MoveNext())
return "";
StringBuilder sb = new StringBuilder();
sb.Append(prefix).Append(enumerator.Current.ToString()).Append(suffix);
while (enumerator.MoveNext())
sb.Append(separator).Append(prefix)
.Append(enumerator.Current.ToString()).Append(suffix);
return sb.ToString();
}
}
IEnumerable<string>
使用分隔符进行连接。您始终可以IEnumerable<string>
在调用此方法之前将数据投影到其中。
null
附加到之前不需要检查StringBuilder
。您需要处置枚举器。您可以摆脱循环Append
上方的调用,并将while
循环替换为do-while
。最后,除非要避免支付创建a的成本,否则StringBuilder
不需要将第一个元素视为特殊情况:new StringBuilder().ToString()
will return string.Empty
。
Aggregate
操作员还没有做的事情吗?这是一种不寻常的用法,但是绝对可以用于连接对象列表。
Aggregate
如果在每个阶段连接字符串而不是使用StringBuilder ,则速度会变慢。但是对于两个人,即使我想使用Aggregate
它,我仍然会将其包装到JoinString
带有此签名的方法中,因为它使使用它的代码更加清晰。一旦有了,我不妨通过使用StringBuilder来编写更快的方法。
/// <summary>Sorts the elements of a sequence in ascending order.</summary>
public static IEnumerable<T> Order<T>(this IEnumerable<T> source)
{
return source.OrderBy(x => x);
}
List<T>.Sort()
List<T>.Sort
它就位。
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
var random = new Random();
return items.OrderBy(x => random.Next());
}
编辑:上面的实现似乎有几个问题。这是基于@LukeH的代码以及@ck和@Strilanc的注释的改进版本。
private static Random _rand = new Random();
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
var items = source == null ? new T[] { } : source.ToArray();
var count = items.Length;
while(count > 0)
{
int toReturn = _rand.Next(0, count);
yield return items[toReturn];
items[toReturn] = items[count - 1];
count--;
}
}
Func<int>
但必须由他人来简化该方法获取其PRN的方式。
我刚刚想到的这有点酷。(如果我只是想到了,也许它没有那么有用?但是我想到了它,因为我有用。)重复遍历一个序列以生成一个无限序列。除了可以用于任意序列(不同于)(不同于)之外,这可以完成某种工作Enumerable.Range
并Enumerable.Repeat
为您提供:Range
Repeat
public static IEnumerable<T> Loop<T>(this IEnumerable<T> source)
{
while (true)
{
foreach (T item in source)
{
yield return item;
}
}
}
用法:
var numbers = new[] { 1, 2, 3 };
var looped = numbers.Loop();
foreach (int x in looped.Take(10))
{
Console.WriteLine(x);
}
输出:
1个 2 3 1个 2 3 1个 2 3 1个
注意:我想您也可以通过以下方式完成此操作:
var looped = Enumerable.Repeat(numbers, int.MaxValue).SelectMany(seq => seq);
...但我认为Loop
更清楚。
Reset
枚举器;但我敢肯定,有90%的IEnumerator<T>
实现都不受支持。因此,我猜您可能会获得“意外的”结果,但前提是您期望某些不可行的结果。换句话说:用于任何有序序列(如T[]
,List<T>
,Queue<T>
等),该命令将是稳定的; 对于任何无序序列,您都不应该期望它是(在我看来)。
bool
参数,该参数指定该方法是否应缓存第一个枚举的顺序,然后对其进行循环。
x.Memoize().Loop()
,但这当然意味着您首先需要一个Memoize
函数。我同意可选的bool可以,也可以使用单独的方法,例如LoopSame
或LoopDeterministic
。
Min
仅返回指定表达式返回的最小值,而不返回给出该最小值的原始元素。
/// <summary>Returns the first element from the input sequence for which the
/// value selector returns the smallest value.</summary>
public static T MinElement<T, TValue>(this IEnumerable<T> source,
Func<T, TValue> valueSelector) where TValue : IComparable<TValue>
{
if (source == null)
throw new ArgumentNullException("source");
if (valueSelector == null)
throw new ArgumentNullException("valueSelector");
using (var enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
throw new InvalidOperationException("source contains no elements.");
T minElem = enumerator.Current;
TValue minValue = valueSelector(minElem);
while (enumerator.MoveNext())
{
TValue value = valueSelector(enumerator.Current);
if (value.CompareTo(minValue) < 0)
{
minValue = value;
minElem = enumerator.Current;
}
}
return minElem;
}
}
valueSelector
returnIComparable
或更改valueSelector
为类似的东西,Func<T, T, bool> lessThan
以便可以比较诸如字符串或小数之类的东西。
/// <summary>
/// Returns the index of the first element in this <paramref name="source"/>
/// satisfying the specified <paramref name="condition"/>. If no such elements
/// are found, returns -1.
/// </summary>
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> condition)
{
if (source == null)
throw new ArgumentNullException("source");
if (condition == null)
throw new ArgumentNullException("condition");
int index = 0;
foreach (var v in source)
{
if (condition(v))
return index;
index++;
}
return -1;
}
FindIndex
来匹配方法,List<T>
并且Array
做同样的事情。我还将考虑检查是否source
已实现它并调用本机FindIndex
函数(尽管这不会对性能产生太大影响,因为您没有带起始索引的重载),这是其中之一。 。
返回特定大小的块。x.Chunks(2)
of1,2,3,4,5
将返回带有1,2
和的两个数组3,4
。x.Chunks(2,true)
将返回1,2
,3,4
并且5
。
public static IEnumerable<T[]> Chunks<T>(this IEnumerable<T> xs, int size, bool returnRest = false)
{
var curr = new T[size];
int i = 0;
foreach (var x in xs)
{
if (i == size)
{
yield return curr;
i = 0;
curr = new T[size];
}
curr[i++] = x;
}
if (returnRest)
yield return curr.Take(i).ToArray();
}
Buffer
IEnumerable<IEnumerable<T>>
。
IEnumerable
版本会慢一些(至少对于小块而言)。但是如上所述,我已经使用了许多上述版本。而且,您可以随时使用数组,IEnumerable
因为数组可以继承IEnumerable
。
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
return new HashSet<T>(items);
}
/// <summary>
/// Returns the first element of a sequence, or a default value if the
/// sequence contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable<T>"/> to return
/// the first element of.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty;
/// otherwise, the first element in <paramref name="source"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source, T @default)
{
if (source == null)
throw new ArgumentNullException("source");
using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
return @default;
return e.Current;
}
}
/// <summary>
/// Returns the first element of a sequence, or a default value if the sequence
/// contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable<T>"/> to return
/// the first element of.</param>
/// <param name="predicate">A function to test each element for a
/// condition.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty
/// or if no element passes the test specified by <paramref name="predicate"/>;
/// otherwise, the first element in <paramref name="source"/> that passes
/// the test specified by <paramref name="predicate"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source,
Func<T, bool> predicate, T @default)
{
if (source == null)
throw new ArgumentNullException("source");
if (predicate == null)
throw new ArgumentNullException("predicate");
using (var e = source.GetEnumerator())
{
while (true)
{
if (!e.MoveNext())
return @default;
if (predicate(e.Current))
return e.Current;
}
}
}
FirstOr
作为val = list.FirstOr(defaultVal)
FirstOrFallback
在每对连续的元素之间插入一个元素。
/// <summary>Inserts the specified item in between each element in the input
/// collection.</summary>
/// <param name="source">The input collection.</param>
/// <param name="extraElement">The element to insert between each consecutive
/// pair of elements in the input collection.</param>
/// <returns>A collection containing the original collection with the extra
/// element inserted. For example, new[] { 1, 2, 3 }.InsertBetween(0) returns
/// { 1, 0, 2, 0, 3 }.</returns>
public static IEnumerable<T> InsertBetween<T>(
this IEnumerable<T> source, T extraElement)
{
return source.SelectMany(val => new[] { extraElement, val }).Skip(1);
}
这是一个有争议的。我相信许多纯粹主义者将在null
成功之后反对“实例方法” 。
/// <summary>
/// Returns an IEnumerable<T> as is, or an empty IEnumerable<T> if it is null
/// </summary>
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
{
return source ?? Enumerable.Empty<T>();
}
用法:
foreach(var item in myEnumerable.EmptyIfNull())
{
Console.WriteLine(item);
}
这涉及到一个自定义委托(可以使用IParser<T>
接口,但是我使用了一个委托,因为它更简单),该委托用于将字符串序列解析为值序列,从而跳过解析失败的元素。
public delegate bool TryParser<T>(string text, out T value);
public static IEnumerable<T> Parse<T>(this IEnumerable<string> source,
TryParser<T> parser)
{
source.ThrowIfNull("source");
parser.ThrowIfNull("parser");
foreach (string str in source)
{
T value;
if (parser(str, out value))
{
yield return value;
}
}
}
用法:
var strings = new[] { "1", "2", "H3llo", "4", "five", "6", "se7en" };
var numbers = strings.Parse<int>(int.TryParse);
foreach (int x in numbers)
{
Console.WriteLine(x);
}
输出:
1个 2 4 6
为此命名是棘手的。我不确定是否Parse
是最好的选择(至少很简单),或者是否ParseWhereValid
会更好。
TryParse
或ParseWhereValid
最适合imo。;)
TryParse
;我唯一担心的是有人可能希望它返回abool
并填充一个out IEnumerable<T>
参数(仅在解析了每个项目时才返回true )。也许ParseWhereValid
是最好的。
这是我的版本,Zip
就像真正的拉链一样。它不会将两个值投影为一个,而是返回一个组合的IEnumerable。超载,跳过右尾和/或左尾是可能的。
public static IEnumerable<TSource> ZipMerge<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
using (var secondEnumerator = second.GetEnumerator())
{
foreach (var item in first)
{
yield return item;
if (secondEnumerator.MoveNext())
yield return secondEnumerator.Current;
}
while (secondEnumerator.MoveNext())
yield return secondEnumerator.Current;
}
}
Zip
?(我知道参数足以区分,但
second
,还是我误读了代码?
MoveNext
。第一次调用将告诉您该枚举数是否为空。Current
然后包含第一个元素。
这是一个简单的函数,如果您有一组中等大小的数据(例如,超过100个项目),并且希望仅对数据进行随机抽样,则很有用。
public static IEnumerable<T> RandomSample<T>(this IEnumerable<T> source,
double percentage)
{
source.ThrowIfNull("source");
var r = new Random();
return source.Where(x => (r.NextDouble() * 100.0) < percentage);
}
用法:
List<DataPoint> data = GetData();
// Sample roughly 3% of the data
var sample = data.RandomSample(3.0);
// Verify results were correct for this sample
foreach (DataPoint point in sample)
{
Console.WriteLine("{0} => {1}", point, DoCalculation(point));
}
笔记:
source.OrderBy(r.NextDouble()).Take(x);
有效地确定an是否IEnumerable<T>
至少包含/准确/最多包含一定数量的元素。
public enum CountAssertion
{
AtLeast,
Exact,
AtMost
}
/// <summary>
/// Asserts that the number of items in a sequence matching a specified predicate satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion, Func<T, bool> predicate)
{
if (source == null)
throw new ArgumentNullException("source");
if (predicate == null)
throw new ArgumentNullException("predicate");
return source.Where(predicate).AssertCount(countToAssert, assertion);
}
/// <summary>
/// Asserts that the number of elements in a sequence satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion)
{
if (source == null)
throw new ArgumentNullException("source");
if (countToAssert < 0)
throw new ArgumentOutOfRangeException("countToAssert");
switch (assertion)
{
case CountAssertion.AtLeast:
return AssertCountAtLeast(source, GetFastCount(source), countToAssert);
case CountAssertion.Exact:
return AssertCountExact(source, GetFastCount(source), countToAssert);
case CountAssertion.AtMost:
return AssertCountAtMost(source, GetFastCount(source), countToAssert);
default:
throw new ArgumentException("Unknown CountAssertion.", "assertion");
}
}
private static int? GetFastCount<T>(IEnumerable<T> source)
{
var genericCollection = source as ICollection<T>;
if (genericCollection != null)
return genericCollection.Count;
var collection = source as ICollection;
if (collection != null)
return collection.Count;
return null;
}
private static bool AssertCountAtMost<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (fastCount.HasValue)
return fastCount.Value <= countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar > countToAssert) return false;
}
return true;
}
private static bool AssertCountExact<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (fastCount.HasValue)
return fastCount.Value == countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar > countToAssert) return false;
}
return countSoFar == countToAssert;
}
private static bool AssertCountAtLeast<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (countToAssert == 0)
return true;
if (fastCount.HasValue)
return fastCount.Value >= countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar >= countToAssert) return true;
}
return false;
}
用法:
var nums = new[] { 45, -4, 35, -12, 46, -98, 11 };
bool hasAtLeast3Positive = nums.AssertCount(3, CountAssertion.AtLeast, i => i > 0); //true
bool hasAtMost1Negative = nums.AssertCount(1, CountAssertion.AtMost, i => i < 0); //false
bool hasExactly2Negative = nums.AssertCount(2, CountAssertion.Exact, i => i < 0); //false
Enumerable.Count<T>()
无法以这种方式确定计数,它将枚举整个序列。这将破坏此扩展的要点,后者可能会快速失败和快速通过。
CountAtMost
这样我就可以根据是否存在,单数或复数元素来更改操作,而不必创建3个枚举数或对所有元素进行计数。
CountAssertion.AtMost
。还是我错过了什么?
Assertion
命名法,因为Assert
对我而言,该词暗示其条件为假时会引发异常。
CountIs
,CountIsAtMost
,和CountIsAtLeast
。
枚举长度为size
包含最新值的数组(“窗口”)。
{ 0, 1, 2, 3 }
成为{ [0, 1], [1, 2], [2, 3] }
。
例如,我使用它通过连接两个点来绘制折线图。
public static IEnumerable<TSource[]> Window<TSource>(
this IEnumerable<TSource> source)
{
return source.Window(2);
}
public static IEnumerable<TSource[]> Window<TSource>(
this IEnumerable<TSource> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size");
return source.Skip(size).WindowHelper(size, source.Take(size));
}
private static IEnumerable<TSource[]> WindowHelper<TSource>(
this IEnumerable<TSource> source, int size, IEnumerable<TSource> init)
{
Queue<TSource> q = new Queue<TSource>(init);
yield return q.ToArray();
foreach (var value in source)
{
q.Dequeue();
q.Enqueue(value);
yield return q.ToArray();
}
}
public static bool One<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
return enumerator.MoveNext() && !enumerator.MoveNext();
}
public static bool Two<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
return enumerator.MoveNext() && enumerator.MoveNext() && !enumerator.MoveNext();
}
public static bool MoreThanOne<T>(this IEnumerable<T> enumerable)
{
return enumerable.Skip(1).Any();
}
public static bool AtLeast<T>(this IEnumerable<T> enumerable, int count)
{
using (var enumerator = enumerable.GetEnumerator())
for (var i = 0; i < count; i++)
if (!enumerator.MoveNext())
return false;
return true;
}
public static bool AnyAtAll<T>(this IEnumerable<T> enumerable)
{
return enumerable != null && enumerable.Any();
}
ToEnumerable
因为它似乎与其他人无关,并且无论如何它已经发布在另一个答案中。
/// <summary>
/// Enumerates the items of this collection, skipping the last
/// <paramref name="count"/> items. Note that the memory usage of this method
/// is proportional to <paramref name="count"/>, but the source collection is
/// only enumerated once, and in a lazy fashion. Also, enumerating the first
/// item will take longer than enumerating subsequent items.
/// </summary>
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return source;
return skipLastIterator(source, count);
}
private static IEnumerable<T> skipLastIterator<T>(IEnumerable<T> source,
int count)
{
var queue = new T[count];
int headtail = 0; // tail while we're still collecting, both head & tail
// afterwards because the queue becomes completely full
int collected = 0;
foreach (var item in source)
{
if (collected < count)
{
queue[headtail] = item;
headtail++;
collected++;
}
else
{
if (headtail == count) headtail = 0;
yield return queue[headtail];
queue[headtail] = item;
headtail++;
}
}
}
/// <summary>
/// Returns a collection containing only the last <paramref name="count"/>
/// items of the input collection. This method enumerates the entire
/// collection to the end once before returning. Note also that the memory
/// usage of this method is proportional to <paramref name="count"/>.
/// </summary>
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return new T[0];
var queue = new Queue<T>(count + 1);
foreach (var item in source)
{
if (queue.Count == count)
queue.Dequeue();
queue.Enqueue(item);
}
return queue.AsEnumerable();
}
TakeLast
可以用于SkipLast
与yield return queue.Dequeue();
。
与Ani的AssertCount
方法(我使用一种称为CountAtLeast
)的方法结合使用,可以很容易地在一个序列中查找出现多次的元素:
public static IEnumerable<T> Duplicates<T, TKey>(this IEnumerable<T> source,
Func<T, TKey> keySelector = null, IEqualityComparer<TKey> comparer = null)
{
source.ThrowIfNull("source");
keySelector = keySelector ?? new Func<T, TKey>(x => x);
comparer = comparer ?? EqualityComparer<TKey>.Default;
return source.GroupBy(keySelector, comparer)
.Where(g => g.CountAtLeast(2))
.SelectMany(g => g);
}
g.CountAtLeast(2)
使用“内置” LINQ编写g.Skip(1).Any()
。
SkipNulls()
,仅仅是Where(x => x != null)
)。我之所以使用它们,不是因为它们做了很多额外的工作,而是因为我发现它们使代码更具可读性(无论如何,将几个方法调用包装到一个适合代码重用的地方也不是一件坏事)。
SkipNulls<T>()
确实是OfType<T>()
。
OfType<T>
或Where<T>
; 无论哪种方式,它都只是一个琐碎的包装器。我的意思是,这个名称SkipNulls
使它更具用途。
可选Where
的条款IEnumerable
和IQueryable
。在为查询构建谓词和lambda时避免if语句。当您在编译时不知道是否应应用过滤器时很有用。
public static IEnumerable<TSource> WhereIf<TSource>(
this IEnumerable<TSource> source, bool condition,
Func<TSource, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
用途:
var custs = Customers.WhereIf(someBool, x=>x.EyeColor=="Green");
mySearchResults.WhereIf(chkShowOnlyUnapproved.Checked, x=>!x.IsApproved)
ToList和ToDictionary重载公开了基础集合类的初始容量。当源长度已知或有界时,有时有用。
public static List<TSource> ToList<TSource>(
this IEnumerable<TSource> source,
int capacity)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var list = new List<TSource>(capacity);
list.AddRange(source);
return list;
}
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
int capacity,
IEqualityComparer<TKey> comparer = null)
{
return source.ToDictionary<TSource, TKey, TSource>(
keySelector, x => x, capacity, comparer);
}
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
int capacity,
IEqualityComparer<TKey> comparer = null)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (keySelector == null)
{
throw new ArgumentNullException("keySelector");
}
if (elementSelector == null)
{
throw new ArgumentNullException("elementSelector");
}
var dictionary = new Dictionary<TKey, TElement>(capacity, comparer);
foreach (TSource local in source)
{
dictionary.Add(keySelector(local), elementSelector(local));
}
return dictionary;
}
static int CountUpTo<T>(this IEnumerable<T> source, int maxCount)
{
if (maxCount == 0)
return 0;
var genericCollection = source as ICollection<T>;
if (genericCollection != null)
return Math.Min(maxCount, genericCollection.Count);
var collection = source as ICollection;
if (collection != null)
return Math.Min(maxCount, collection.Count);
int count = 0;
foreach (T item in source)
if (++count >= maxCount)
break;
return count;
}
collection.Take(maxCount).Count()
,对吗?
public static T Coalesce<T>(this IEnumerable<T> items) {
return items.Where(x => x != null && !x.Equals(default(T))).FirstOrDefault();
// return items.OfType<T>().FirstOrDefault(); // Gabe's take
}
Nothing
不等同于null
……不得不修改C#实现。