What's your favorite LINQ to Objects operator which is not built-in? [closed]
开发者_C百科
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this questionWith extension methods, we can write handy LINQ operators which solve generic problems.
I want to hear which methods or overloads you are missing in the System.Linq
namespace and how you implemented them.
Clean and elegant implementations, maybe using existing methods, are preferred.
Append & Prepend
(These have been added to .NET since this answer was written.)
/// <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;
}
I'm surprised no one has mentioned the MoreLINQ project yet. It was started by Jon Skeet and has gained some developers along the way. From the project's page:
LINQ to Objects is missing a few desirable features.
This project will enhance LINQ to Objects with extra methods, in a manner which keeps to the spirit of LINQ.
Take a look at the Operators Overview wiki page for a list of implemented operators.
It is certainly a good way to learn from some clean and elegant source code.
Each
Nothing for the purists, but darn it's useful!
public static void Each<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var i in items)
action(i);
}
ToQueue & ToStack
/// <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);
}
IsEmpty
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
return !source.Any();
}
In and NotIn
C# equivalents of two other well-known SQL constructs
/// <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;
}
AsIEnumerable
/// <summary>
/// Returns a sequence containing one element.
/// </summary>
public static IEnumerable<T> AsIEnumerable<T>(this T obj)
{
yield return obj;
}
Usage:
var nums = new[] {12, 20, 6};
var numsWith5Prepended = 5.AsIEnumerable().Concat(nums);
JoinString
Basically the same as string.Join
, but:
with the ability to use it on any collection, not just a collection of strings (calls
ToString
on every element)with the ability to add a prefix and suffix to every string.
as an extension method. I find
string.Join
annoying because it is static, meaning that in a chain of operations it is lexically not in the correct order.
/// <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();
}
}
Order
/// <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);
}
Shuffle
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
var random = new Random();
return items.OrderBy(x => random.Next());
}
EDIT: It seems there are several issues with the above implementation. Here is an improved version based @LukeH's code and comments from @ck and @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--;
}
}
Loop
Here's a kinda cool one I just thought of. (If I just thought of it, maybe it's not that useful? But I thought of it because I have a use for it.) Loop through a sequence repeatedly to generate an infinite sequence. This accomplishes something kind of like what Enumerable.Range
and Enumerable.Repeat
give you, except it can be used for an arbitrary (unlike Range
) sequence (unlike Repeat
):
public static IEnumerable<T> Loop<T>(this IEnumerable<T> source)
{
while (true)
{
foreach (T item in source)
{
yield return item;
}
}
}
Usage:
var numbers = new[] { 1, 2, 3 };
var looped = numbers.Loop();
foreach (int x in looped.Take(10))
{
Console.WriteLine(x);
}
Output:
1 2 3 1 2 3 1 2 3 1
Note: I suppose you could also accomplish this with something like:
var looped = Enumerable.Repeat(numbers, int.MaxValue).SelectMany(seq => seq);
...but I think Loop
is clearer.
MinElement
Min
only returns the minimum value returned by the specified expression, but not the original element that gave this minimum element.
/// <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;
}
}
IndexOf
/// <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;
}
Chunks
Returns chunks of a specific size. x.Chunks(2)
of 1,2,3,4,5
will return two arrays with 1,2
and 3,4
. x.Chunks(2,true)
will return 1,2
, 3,4
and 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();
}
ToHashSet
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
return new HashSet<T>(items);
}
FirstOrDefault with a default value specified
/// <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;
}
}
}
InsertBetween
Inserts an element in between every pair of consecutive elements.
/// <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);
}
EmptyIfNull
This is a controversial one; I am sure many purists will object to an "instance method" on null
succeeding.
/// <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>();
}
Usage:
foreach(var item in myEnumerable.EmptyIfNull())
{
Console.WriteLine(item);
}
Parse
This one involves a custom delegate (could've used an IParser<T>
interface instead, but I went with a delegate as it was simpler), which is used to parse a sequence of strings to a sequence of values, skipping the elements where parsing fails.
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;
}
}
}
Usage:
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);
}
Output:
1 2 4 6
Naming's tricky for this one. I'm not sure whether Parse
is the best option (it is simple, at least), or if something like ParseWhereValid
would be better.
ZipMerge
This is my version of Zip
which works like a real zipper. It does not project two values into one but returns a combined IEnumerable. Overloads, skipping the right and/or left tail are possible.
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;
}
}
RandomSample
Here's a simple function that's useful if you have a medium-large set of data (say, over 100 items) and you want to eyeball just a random sampling of it.
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);
}
Usage:
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));
}
Notes:
- Not really appropriate for tiny collections as the number of items returned is probabilistic (could easily return zero on a small sequence).
- Not really appropriate for huge collections or database queries as it involves enumerating over every item in the sequence.
AssertCount
Efficiently determines if an an IEnumerable<T>
contains at least / exactly / at most a certain number of elements.
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;
}
Usage:
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
Window
Enumerates arrays ("windows") with the length of size
containing the most current values.
{ 0, 1, 2, 3 }
becomes to { [0, 1], [1, 2], [2, 3] }
.
I am using this for example to draw a line graph by connecting two points.
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();
}
}
One, Two, MoreThanOne, AtLeast, AnyAtAll
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();
}
SkipLast & TakeLast
/// <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();
}
Duplicates
Used in conjunction with a method like Ani's AssertCount
method (I use one called CountAtLeast
), it becomes very easy to find elements in a sequence that appear more than once:
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);
}
WhereIf
Optional Where
clause on IEnumerable
and IQueryable
. Avoids if statements when building predicates & lambdas for a query. Useful when you don't know at compile time whether a filter should apply.
public static IEnumerable<TSource> WhereIf<TSource>(
this IEnumerable<TSource> source, bool condition,
Func<TSource, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
Useage:
var custs = Customers.WhereIf(someBool, x=>x.EyeColor=="Green");
LINQ WhereIf At ExtensionMethod.NET and borrowed from Andrew's blog.
ToList and ToDictionary with Initial Capacity
ToList and ToDictionary overloads that expose the underlying collection classes' initial capacity. Occasionally useful when source length is known or bounded.
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;
}
CountUpTo
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;
}
Coalesce
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
}
精彩评论