yield
был разработан для C#2 (перед Linq в C#3).
Мы использовали его в большой степени в веб-приложении крупного предприятия C#2 при контакте с доступом к данным и в большой степени повторили вычисления.
Наборы являются большими любое время, у Вас есть несколько элементов, которые Вы собираетесь поразить многократно.
Однако в большом количестве сценариев доступа к данным у Вас есть большие количества элементов, которые необходимо не обязательно передать по кругу в большом большом наборе.
Это по существу, что эти SqlDataReader
делает - это - вперед только пользовательский перечислитель.
, Чему yield
позволяет, Вы сделать быстро, и с минимальным кодом пишут Ваши собственные перечислители.
Все yield
делает мог быть сделан в C#1 - он просто взял стопки кода, чтобы сделать это.
Linq действительно максимизирует значение поведения урожая, но это, конечно, не единственное приложение.
LINQ ...
.SkipWhile (предикат)
(оставлено неопределенным, поскольку ваше значение неясно) .TakeWhile (предикат)
(слева неопределенно поскольку ваш смысл не ясен) .Last ()
.First ()
.Where ((x, i) => i% 2 == 1)
.Where ((x, i) => i% 2 == 0)
Джон Скит написал для этой цели SmartEnumerable . Его легко расширить, чтобы предоставить свойства IsOdd
и IsEven
.
Ответ Марка Гравелла хорош, потому что он прост, но он будет менее производительным в тех случаях, когда вы сначала хотите что-то сделать на все нечетные элементы, а затем все четные элементы, так как это потребовало бы перебора всех элементов дважды, в то время как только один раз строго необходим.
Я полагаю, вы могли бы написать функцию из элемента в какое-то перечисление и сгруппировать все элементы по значению перечисления, которому они сопоставляются. Тогда вы легко сможете работать с каждой группой по отдельности. Но я не уверен, как это будет работать, так как я не уверен, что конкретно делает группировка LINQ и насколько она отложена.
public class NavigationItem<T>
{
readonly T _value;
readonly bool _isFirst, _isLast, _isEven;
internal NavigationItem(T value, bool isFirst, bool isLast, bool isEven)
{
_value = value;
_isFirst = isFirst;
_isLast = isLast;
_isEven = isEven;
}
public T Value { get { return _value; } }
public bool IsFirst { get { return _isFirst; } }
public bool IsLast { get { return _isLast; } }
public bool IsEven { get { return _isEven; } }
public bool IsOdd { get { return !_isEven; } }
}
public static class CollectionNavigation
{
public IEnumerable<NavigationItem<T>> ToNavigable<T>(this IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException("collection");
return ToNavigableCore(collection);
}
private IEnumerable<NavigationItem<T>> ToNavigableCore<T>(IEnumerable<T> collection)
{
using(var enumerator = collection.GetEnumerator())
{
if (enumerator.MoveNext())
{
T current = enumerator.Current;
bool isFirst = true, isEven = true;
while(enumerator.MoveNext())
{
yield return new NavigationItem<T>(current, isFirst, false, isEven);
isFirst = false;
isEven = !isEven;
current = enumerator.Current;
}
yield return new NavigationItem<T>(current, isFirst, true, isEven);
}
}
}
}
Использование:
foreach(var item in collection.ToNavigable())
{
if (item.IsFirst) { ... }
if (item.IsLast) { ... }
if (item.IsEven) { ... }
if (item.IsOdd) { ... }
Console.WriteLine(item.Value);
}
Конечно, он должен быть общим, чтобы вам не приходилось разыгрывать ценности. Вы можете использовать делегат Action во фреймворке вместо объявления своего собственного.
public enum ForEachExecuction {
Concurrent,
Seperate
}
public static class ForEach<T> {
private static bool Call(Action<T> function, bool condition, T value, Action<T> concurrent) {
condition &= function != null;
if (condition) {
function(value);
if (concurrent != null) concurrent(value);
}
return condition;
}
public static void Loop(
IList<T> collection,
Action<T> function,
Action<T> before,
Action<T> first,
Action<T> evens,
Action<T> odds,
Action<T> last,
Action<T> after,
ForEachExecuction when)
{
Action<T> concurrent = when == ForEachExecuction.Concurrent?function:null;
for (int i = 0; i < collection.Count; i++) {
T value = collection[i];
Call(before, i == 0, value, null);
if (!Call(first, i == 0, value, concurrent)) {
if (!Call(evens, i % 2 != 0, value, concurrent)) {
if (!Call(odds, i % 2 == 0, value, concurrent)) {
if (!Call(last, i==collection.Count-1, value, concurrent)) {
function(value);
}
}
}
}
Call(after, i == collection.Count - 1, value, null);
}
}
}
Вызов:
string[] testCollection = { "1", "2", "3", "4", "5" };
Action<string> primaryFunction = delegate(string s) { Console.WriteLine(s); }
Action<string> first = delegate(string s) { Console.WriteLine("first"); }
Action<string> odd = delegate(string s) { Console.WriteLine("odd"); }
Action<string> after = delegate(string s) { Console.WriteLine("after"); }
ForEach<string>.Loop(testCollection, primaryFunction, null, first, null, odd, null, after, ForEachExecuction.Concurrent);
Или, возможно:
ForEach<string>.Loop(testCollection,
(s) => Console.WriteLine(s),
null,
(s) => Console.WriteLine("first"),
null,
(s) => Console.WriteLine("odd"),
null,
(s) => Console.WriteLine("after"),
ForEachExecuction.Concurrent
);
for (;;) {}
Серьезно.
* PS: Условия вроде Odd и After зависят от строгого и значимого порядка, что делает foreach неправильным как по значению, так и по синтаксису. Нужно знать, когда перестать следовать моде. Обратите внимание, как предлагаемый LINQ .SkipWhile (предикат) семантически отличается от After (index) *
Поддерживая все ваши требования одним очень гибким методом, вы в конечном итоге получаете кодовый эквивалент швейцарского армейского ножа.
Лучше предоставить все эти возможности в виде отдельных компонентов, которые вы можете составить для каждой ситуации.
Linq предоставляет отличную отправную точку. Подумайте о foreach
как о способе преобразования списка объектов в список действий. Воспринимайте это буквально: последовательность делегатов Action
или IEnumerable
.
Для удобного выполнения последовательности действий вам необходимо:
public static void Execute(this IEnumerable<Action> actions)
{
foreach (var a in actions)
a();
}
Теперь проблема проста чтобы обеспечить управление последовательностью (большая часть которой уже находится в Linq), чтобы вы могли составить список действий в соответствии с вашими требованиями, чтобы вы могли поставить Execute
в конце.
My Code, not elegant.
public enum forEachExeuction
{
Concurrent,
Seperate
}
public delegate void forEachDelegate(object o);
public static void forEach(
IList collection,
forEachDelegate function,
forEachDelegate before,
forEachDelegate first,
forEachDelegate evens,
forEachDelegate odds,
forEachDelegate last,
forEachDelegate after,
forEachExeuction when)
{
bool doBefore = before != null;
bool doFirst = first != null;
bool doEvens = evens != null;
bool doOdds = odds != null;
bool doLast = last != null;
bool doAfter = after != null;
bool conCurrent = when == forEachExeuction.Concurrent;
int collectionCount = collection.Count;
for (int i = 0; i < collectionCount; i++)
{
if (doBefore && i == 0)
{
before(collection[i]);
}
if (doFirst && i == 0)
{
first(collection[i]);
if (conCurrent)
function(collection[i]);
}
else if (doEvens && i % 2 != 0)
{
evens(collection[i]);
if (conCurrent)
function(collection[i]);
}
else if (doOdds && i % 2 == 0)
{
odds(collection[i]);
if (conCurrent)
function(collection[i]);
}
else if (doLast && i == collectionCount - 1)
{
last(collection[i]);
if (conCurrent)
function(collection[i]);
}
else
{
function(collection[i]);
}
if (after != null && i == collectionCount - 1)
{
after(collection[i]);
}
}
}
Simple call to it:
string[] testCollection = {"1", "2", "3", "4", "5"};
forEachDelegate primaryFunction = delegate(object o)
{
Response.Write(o.ToString());
};
forEachDelegate first = delegate(object o)
{
Response.Write("first");
};
forEachDelegate odd = delegate(object o)
{
Response.Write("odd");
};
forEachDelegate after = delegate(object o)
{
Response.Write("after");
};
Randoms.forEach(testCollection, primaryFunction, null, first, null, odd, null, after, Randoms.forEachExeuction.Concurrent);
You would need to cast each object in each delegate to the proper object. Default parameters and named parameters would make this look much better. /wait for C# 4.0