715 lines
24 KiB
C#
715 lines
24 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
|
|
namespace BinaryDad.Extensions
|
|
{
|
|
public static class CollectionExtensions
|
|
{
|
|
public static void AddReplace<TItem, TProperty>(this ICollection<TItem> items, TItem item, Func<TItem, TProperty> matchingProperty) where TItem : class where TProperty : IComparable
|
|
{
|
|
var existing = items.FirstOrDefault(i => matchingProperty(i).Equals(matchingProperty(item)));
|
|
|
|
// if existing exists, replace existing
|
|
if (existing != null)
|
|
{
|
|
items.Remove(existing);
|
|
}
|
|
|
|
items.Add(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a distinct list of elements using the first-matched item on a specific property
|
|
/// </summary>
|
|
/// <typeparam name="TItem"></typeparam>
|
|
/// <typeparam name="TProperty"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <param name="property">Property of object to compare</param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<TItem> Distinct<TItem, TProperty>(this IEnumerable<TItem> items, Func<TItem, TProperty> property) where TProperty : IComparable
|
|
{
|
|
return items
|
|
.GroupBy(property)
|
|
.Select(i => i.FirstOrDefault());
|
|
}
|
|
|
|
public static IEnumerable<T> Concat<T>(this IEnumerable<T> items, T additionalValue)
|
|
{
|
|
if (items == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(items));
|
|
}
|
|
|
|
return items.Concat(new[]
|
|
{
|
|
additionalValue
|
|
});
|
|
}
|
|
|
|
#region ToDataTable
|
|
|
|
/// <summary>
|
|
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="collection"></param>
|
|
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param> or <see cref="PropertyAliasAttribute"/>, if bound to a property.
|
|
/// <param name="tableName"></param>
|
|
/// <returns></returns>
|
|
public static DataTable ToDataTable<T>(this IEnumerable<T> collection, bool useColumnAttributeName = false, string tableName = null) where T : class
|
|
{
|
|
using (var table = new DataTable(tableName))
|
|
{
|
|
#region Build Table Schema
|
|
|
|
var propertyInfo = typeof(T)
|
|
.GetProperties()
|
|
.Select(p => new
|
|
{
|
|
Property = p,
|
|
|
|
// set column name to be either the property name
|
|
// or, if specified, based on the attribute
|
|
ColumnName = useColumnAttributeName
|
|
? p.GetDataColumnNames().FirstOrDefault()
|
|
: p.Name,
|
|
|
|
// include the column if [NotMapped] is NOT attached
|
|
IncludeColumn = !p.HasCustomAttribute<NotMappedAttribute>()
|
|
})
|
|
.Where(p => p.IncludeColumn)
|
|
.ToList();
|
|
|
|
foreach (var info in propertyInfo)
|
|
{
|
|
var columnType = info.Property.PropertyType;
|
|
|
|
if (columnType.IsGenericType)
|
|
{
|
|
columnType = columnType.GetGenericArguments()[0];
|
|
}
|
|
|
|
table.Columns.Add(info.ColumnName, columnType);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Populate the rows
|
|
|
|
foreach (var item in collection)
|
|
{
|
|
var row = table.NewRow();
|
|
|
|
foreach (var info in propertyInfo)
|
|
{
|
|
var value = info.Property.GetValue(item, null);
|
|
|
|
if (value != null)
|
|
{
|
|
row[info.ColumnName] = value;
|
|
}
|
|
}
|
|
|
|
table.Rows.Add(row);
|
|
}
|
|
|
|
#endregion
|
|
|
|
return table;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Join
|
|
|
|
public static string Join<T>(this IEnumerable<T> items, string separator = "") where T : IComparable
|
|
{
|
|
if (items == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(items));
|
|
}
|
|
|
|
if (separator == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(separator));
|
|
}
|
|
|
|
return string.Join(separator, items.ToArray());
|
|
}
|
|
|
|
public static string Join<T, TSelector>(this IEnumerable<T> items, Func<T, TSelector> selector, string separator = "") where TSelector : IComparable => Join(items.Select(selector), separator);
|
|
|
|
/// <summary>
|
|
/// Performs a simple join of list type T, returning items from source only
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="outer"></param>
|
|
/// <param name="inner"></param>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<T> Join<T>(this IEnumerable<T> outer, IEnumerable<T> inner, Func<T, object> key) => outer.Join(inner, key, key, (t, i) => t);
|
|
|
|
#endregion
|
|
|
|
#region Zip
|
|
|
|
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> combine, bool symmetric)
|
|
{
|
|
if (first == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(first));
|
|
}
|
|
|
|
if (second == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(second));
|
|
}
|
|
|
|
if (combine == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(combine));
|
|
}
|
|
|
|
var iter1 = first.GetEnumerator();
|
|
var iter2 = second.GetEnumerator();
|
|
|
|
var mn1 = iter1.MoveNext();
|
|
var mn2 = iter2.MoveNext();
|
|
|
|
while ((symmetric && mn1 && mn2) || (!symmetric && (mn1 || mn2)))
|
|
{
|
|
var c1 = default(TFirst);
|
|
var c2 = default(TSecond);
|
|
|
|
if (mn1)
|
|
{
|
|
c1 = iter1.Current;
|
|
mn1 = iter1.MoveNext();
|
|
}
|
|
|
|
if (mn2)
|
|
{
|
|
c2 = iter2.Current;
|
|
mn2 = iter2.MoveNext();
|
|
}
|
|
|
|
yield return combine(c1, c2);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RemoveEmpty
|
|
|
|
public static IEnumerable<T> RemoveEmpty<T>(this IEnumerable<T> list) where T : class
|
|
{
|
|
if (list == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(list));
|
|
}
|
|
|
|
return list.Where(i => i != null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Apply
|
|
|
|
public static T Apply<T>(this IEnumerable<Func<T, T>> functions, T input)
|
|
{
|
|
if (functions == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(functions));
|
|
}
|
|
|
|
functions.ForEach(f => input = f(input));
|
|
|
|
return input;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Where
|
|
|
|
public static IEnumerable<T> Where<T>(this IEnumerable<T> list, IEnumerable<Func<T, bool>> filters)
|
|
{
|
|
if (list == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(list));
|
|
}
|
|
|
|
if (filters == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(filters));
|
|
}
|
|
|
|
filters.ForEach(f => list = list.Where(f));
|
|
|
|
return list;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ForEach
|
|
|
|
public static void ForEach<T>(this IEnumerable<T> list, Action<T> action)
|
|
{
|
|
if (list == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (action == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(action));
|
|
}
|
|
|
|
foreach (var i in list)
|
|
{
|
|
action(i);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Traverse
|
|
|
|
public static IEnumerable<TYield> Traverse<TYield, T>(this IEnumerable<T> source, Func<T, IEnumerable<TYield>> getYield, Func<T, IEnumerable<T>> getChildren)
|
|
{
|
|
if (source == null)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (getYield == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(getYield));
|
|
}
|
|
|
|
if (getChildren == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(getChildren));
|
|
}
|
|
|
|
foreach (var item in source)
|
|
{
|
|
var yields = getYield(item);
|
|
|
|
if (yields != null)
|
|
{
|
|
foreach (var yield in yields)
|
|
{
|
|
yield return yield;
|
|
}
|
|
}
|
|
|
|
var children = getChildren(item);
|
|
|
|
if (children != null)
|
|
{
|
|
foreach (var child in Traverse(children, getYield, getChildren))
|
|
{
|
|
yield return child;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> recurse)
|
|
{
|
|
if (source == null)
|
|
{
|
|
yield break;
|
|
}
|
|
if (recurse == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(recurse));
|
|
}
|
|
|
|
foreach (var item in source)
|
|
{
|
|
yield return item;
|
|
|
|
var children = recurse(item);
|
|
|
|
if (children != null)
|
|
{
|
|
foreach (var child in Traverse(children, recurse))
|
|
{
|
|
yield return child;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region JoinAction
|
|
|
|
/// <summary>
|
|
/// Performs an action on a joined set of lists of the same type
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="left"></param>
|
|
/// <param name="right"></param>
|
|
/// <param name="key"></param>
|
|
/// <param name="assignment"></param>
|
|
public static void JoinAction<T>(this IEnumerable<T> left, IEnumerable<T> right, Func<T, object> key, Action<T, T> assignment) => left.JoinAction(right, key, key, assignment);
|
|
|
|
/// <summary>
|
|
/// Performs an action on a joined set of lists of a different type
|
|
/// </summary>
|
|
/// <typeparam name="TLeft"></typeparam>
|
|
/// <typeparam name="TRight"></typeparam>
|
|
/// <param name="left"></param>
|
|
/// <param name="right"></param>
|
|
/// <param name="leftKey"></param>
|
|
/// <param name="rightKey"></param>
|
|
/// <param name="assignment"></param>
|
|
public static void JoinAction<TLeft, TRight>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, object> leftKey, Func<TRight, object> rightKey, Action<TLeft, TRight> assignment)
|
|
{
|
|
left
|
|
.Join(right, leftKey, rightKey, (l, r) => new { Left = l, Right = r })
|
|
.ForEach(j => assignment(j.Left, j.Right));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs an action on a group joined set of lists of a different type
|
|
/// </summary>
|
|
/// <typeparam name="TLeft"></typeparam>
|
|
/// <typeparam name="TRight"></typeparam>
|
|
/// <param name="left"></param>
|
|
/// <param name="right"></param>
|
|
/// <param name="leftKey"></param>
|
|
/// <param name="rightKey"></param>
|
|
/// <param name="assignment"></param>
|
|
public static void GroupJoinAction<TLeft, TRight>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, object> leftKey, Func<TRight, object> rightKey, Action<TLeft, IEnumerable<TRight>> assignment)
|
|
{
|
|
left
|
|
.GroupJoin(right, leftKey, rightKey, (l, r) => new { Left = l, Right = r })
|
|
.ForEach(j => assignment(j.Left, j.Right));
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Returns a collection of items containing the items of a second collection
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="source"></param>
|
|
/// <param name="items"></param>
|
|
/// <param name="comparer"></param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<T> Containing<T>(this IEnumerable<T> source, IEnumerable<T> items, IEqualityComparer<T> comparer = null) => source.Where(s => items.Contains(s, comparer));
|
|
|
|
public static bool Contains(this string[] source, string value, StringComparison comparison) => source.AnyAndNotNull(s => s.IndexOf(value, comparison) >= 0);
|
|
|
|
public static IEnumerable<T> Convert<T>(this IEnumerable source) where T : IConvertible
|
|
{
|
|
return source
|
|
.Cast<object>()
|
|
.Select(s => s.To<T>());
|
|
}
|
|
|
|
public static TSource FirstOfType<TSource>(this IEnumerable source)
|
|
{
|
|
return source
|
|
.OfType<TSource>()
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
public static bool Matches<T>(this ICollection<T> source, ICollection<T> items, IEqualityComparer<T> comparer = null) where T : IComparable
|
|
{
|
|
return source.Count == items.Count
|
|
&& !source.Except(items, comparer).Any()
|
|
&& !items.Except(source, comparer).Any();
|
|
}
|
|
|
|
public static IEnumerable<T> Insert<T>(this IEnumerable<T> items, int index, T item)
|
|
{
|
|
var count = 0;
|
|
|
|
foreach (var i in items)
|
|
{
|
|
if (count == index)
|
|
{
|
|
yield return item;
|
|
}
|
|
|
|
yield return i;
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source, IEqualityComparer<TKey> comparer = null) => source.ToDictionary(k => k.Key, v => v.Value, comparer);
|
|
|
|
#region Replace
|
|
|
|
/// <summary>
|
|
/// Replaces an item at index with replacement
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <param name="index">Index for item getting replaced</param>
|
|
/// <param name="replacement">Replacement item</param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<T> Replace<T>(this IEnumerable<T> items, int index, T replacement) => items.Replace(index, t => replacement);
|
|
|
|
/// <summary>
|
|
/// Replaces an item at index with replacement (lambda)
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <param name="index">Index for item getting replaced</param>
|
|
/// <param name="replacement">Replacement lambda</param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<T> Replace<T>(this IEnumerable<T> items, int index, Func<T, T> replacement)
|
|
{
|
|
var count = 0;
|
|
|
|
foreach (var i in items)
|
|
{
|
|
if (count == index)
|
|
{
|
|
yield return replacement(i);
|
|
}
|
|
else
|
|
{
|
|
yield return i;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public static IEnumerable<T> RandomTake<T>(this ICollection<T> collection, int take) => RandomTake(collection, collection.Count, take);
|
|
|
|
public static IEnumerable<T> RandomTake<T>(this IEnumerable<T> items, int collectionCount, int take)
|
|
{
|
|
var rand = new Random();
|
|
var needed = take;
|
|
var available = collectionCount;
|
|
|
|
foreach (var i in items)
|
|
{
|
|
if (needed == 0)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (rand.NextDouble() < (double)needed / available)
|
|
{
|
|
yield return i;
|
|
needed--;
|
|
}
|
|
|
|
available--;
|
|
}
|
|
}
|
|
|
|
public static T RandomFirstOrDefault<T>(this IEnumerable<T> items) => RandomShuffle(items).FirstOrDefault();
|
|
|
|
public static T RandomFirstOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate) => RandomShuffle(items).Where(predicate).FirstOrDefault();
|
|
|
|
public static void Swap<T>(this IList<T> list, int indexX, int indexY)
|
|
{
|
|
if (list == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(list));
|
|
}
|
|
|
|
if (indexX < 0 || indexX >= list.Count)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(indexX));
|
|
}
|
|
if (indexY < 0 || indexY >= list.Count)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(indexY));
|
|
}
|
|
|
|
var tmp = list[indexX];
|
|
list[indexX] = list[indexY];
|
|
list[indexY] = tmp;
|
|
}
|
|
|
|
/// <remarks>Knuth-Fisher-Yates shuffle algorithm, see http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html
|
|
/// </remarks>
|
|
public static ICollection<T> RandomShuffle<T>(this IEnumerable<T> collection)
|
|
{
|
|
var rnd = new Random();
|
|
var list = collection.ToList();
|
|
|
|
for (var i = list.Count - 1; i > 0; i--)
|
|
{
|
|
var n = rnd.Next(i + 1);
|
|
list.Swap(i, n);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public static int IndexOf<T>(this IEnumerable<T> items, T value, IEqualityComparer<T> comparer)
|
|
{
|
|
if (items == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(items));
|
|
}
|
|
|
|
if (comparer == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(comparer));
|
|
}
|
|
|
|
var index = 0;
|
|
foreach (var item in items)
|
|
{
|
|
if (comparer.Equals(item, value))
|
|
{
|
|
return index;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public static int IndexOf<T>(this IEnumerable<T> items, T value)
|
|
{
|
|
if (items == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(items));
|
|
}
|
|
|
|
return items.IndexOf(value, EqualityComparer<T>.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether a sequence has any elements. Null collections return false.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <param name="predicate"></param>
|
|
/// <returns></returns>
|
|
public static bool AnyAndNotNull<T>(this IEnumerable<T> items, Func<T, bool> predicate = null)
|
|
{
|
|
if (items == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return predicate != null ? items.Any(predicate) : items.Any();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether a sequence is null or is an empty set.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <param name="predicate"></param>
|
|
/// <returns></returns>
|
|
public static bool NoneOrNull<T>(this IEnumerable<T> items, Func<T, bool> predicate = null) => !items.AnyAndNotNull(predicate);
|
|
|
|
/// <summary>
|
|
/// If list has one item, display singular string. Otherwise, display plural string.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="list"></param>
|
|
/// <param name="plural">Word to be used if not 1 item count</param>
|
|
/// <param name="singular">Word to be used if only 1 item count</param>
|
|
/// <returns></returns>
|
|
public static string IfPlural<T>(this IEnumerable<T> list, string plural, string singular)
|
|
{
|
|
var items = list.ToList();
|
|
|
|
return items.AnyAndNotNull() && items.Count() == 1 ? singular : plural;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes an inline conditional statement if the sequence contains at least one element
|
|
/// </summary>
|
|
/// <typeparam name="TSource"></typeparam>
|
|
/// <typeparam name="TResult"></typeparam>
|
|
/// <param name="list"></param>
|
|
/// <param name="anyResult"></param>
|
|
/// <param name="noneResult"></param>
|
|
/// <returns></returns>
|
|
public static TResult IfAny<TSource, TResult>(this IEnumerable<TSource> list, Func<IEnumerable<TSource>, TResult> anyResult, Func<IEnumerable<TSource>, TResult> noneResult)
|
|
{
|
|
if (list.AnyAndNotNull())
|
|
{
|
|
return anyResult(list);
|
|
}
|
|
|
|
return noneResult(list);
|
|
}
|
|
|
|
#region OrderBy
|
|
|
|
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool ascending)
|
|
{
|
|
return ascending
|
|
? source.OrderBy(keySelector)
|
|
: source.OrderByDescending(keySelector);
|
|
}
|
|
|
|
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string key) => source.OrderBy(key, true);
|
|
|
|
public static IEnumerable<T> OrderByDescending<T>(this IEnumerable<T> source, string key) => source.OrderBy(key, false);
|
|
|
|
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string key, bool ascending)
|
|
{
|
|
// i => i.SomeProperty
|
|
// "i" is a parameter
|
|
// "i.SomeProperty" is the body of the expression
|
|
var param = Expression.Parameter(typeof(T), "i");
|
|
var body = Expression.Property(param, key);
|
|
|
|
// create expression using body and parameters
|
|
var expression = Expression.Lambda<Func<T, object>>(body, param).Compile();
|
|
|
|
return source.OrderBy(expression, ascending);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Sort
|
|
|
|
public static IEnumerable<T> Sort<T>(this IEnumerable<T> items) where T : IComparable<T> => items.OrderBy(i => i);
|
|
|
|
public static IEnumerable<T> SortDescending<T>(this IEnumerable<T> items) where T : IComparable<T> => items.OrderByDescending(i => i);
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Returns an empty collection if collection is null
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="items"></param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> items) => items ?? Enumerable.Empty<T>();
|
|
|
|
/// <summary>
|
|
/// Retrieves form data in a key/value pair collection
|
|
/// </summary>
|
|
/// <param name="collection"></param>
|
|
/// <returns></returns>
|
|
public static IEnumerable<KeyValuePair<string, string>> GetParameters(this NameValueCollection collection)
|
|
{
|
|
foreach (string k in collection.Keys)
|
|
{
|
|
yield return new KeyValuePair<string, string>(k, collection[k]);
|
|
}
|
|
}
|
|
}
|
|
} |