Как лучше всего преобразовать IEnumerator в универсальный IEnumerator?
-
06-07-2019 - |
Вопрос
Я пишу специальную коллекцию ConfigurationElementCollection для пользовательского ConfigurationHandler в C#.NET 3.5 и хочу представить IEnumerator как универсальный IEnumerator.
Каким будет лучший способ добиться этого?
В настоящее время я использую код:
public new IEnumerator<GenericObject> GetEnumerator() { var list = new List(); var baseEnum = base.GetEnumerator(); while(baseEnum.MoveNext()) { var obj = baseEnum.Current as GenericObject; if (obj != null) list.Add(obj); } return list.GetEnumerator(); }
Ваше здоровье
Решение
Я не верю, что в рамках есть что-то, но вы можете легко написать это:
IEnumerator<T> Cast<T>(IEnumerator iterator)
{
while (iterator.MoveNext())
{
yield return (T) iterator.Current;
}
}
Соблазнительно просто позвонить Enumerable.Cast<T>
из LINQ, а затем позвоните GetEnumerator()
от результата - но если ваш класс уже реализует IEnumerable<T>
и T
— это тип значения, который действует как неактивный, поэтому GetEnumerator()
вызов рекурсивно и выдает StackOverflowException
.Это безопасно использовать return foo.Cast<T>.GetEnumerator();
когда foo
определенно является другой объект (который не делегирует обратно этот объект), но в противном случае вам, вероятно, лучше использовать приведенный выше код.
Другие советы
IEnumerable<T>
уже происходит от IEnumerable
поэтому нет необходимости делать какие-либо преобразования.Вы можете просто выполнить приведение к нему... ну, на самом деле это неявно, приведение не требуется.
IEnumerable<T> enumerable = GetGenericFromSomewhere();
IEnumerable sadOldEnumerable = enumerable;
return sadOldEnumerable.GetEnumerator();
С LINQ пойти наоборот не намного сложнее:
var fancyEnumerable = list.OfType<GenericObject>();
return fancyEnumerable.GetEnumerator();
Я столкнулся с той же проблемой переполнения стека, о которой упоминалось в некоторых комментариях.В моем случае это произошло из-за того, что вызов GetEnumerator должен был относиться к base.GetEnumerator, иначе вы зацикливаетесь внутри своего собственного переопределения GetEnumerator.
Это код, который вызывал переполнение стека.Использование оператора foreach вызывает ту же функцию GetEnumerator, которую я пытаюсь перегрузить:
public new IEnumerator<T> GetEnumerator()
{
foreach (T type in this)
{
yield return type;
}
}
В итоге я получил упрощенную версию исходного сообщения, поскольку вам не нужно использовать держатель списка.
public class ElementCollection<T> : ConfigurationElementCollection, IList<T>
...
public new IEnumerator<T> GetEnumerator()
{
var baseEnum = base.GetEnumerator();
while (baseEnum.MoveNext())
{
yield return baseEnum.Current as T;
}
}
...
}
Вы можете использовать OfType<T>
и Cast<T>
.
public static IEnumerable Digits()
{
return new[]{1, 15, 68, 1235, 12390, 1239};
}
var enumerable = Digits().OfType<int>();
foreach (var item in enumerable)
// var is here an int. Without the OfType<int(), it would be an object
Console.WriteLine(i);
Чтобы получить IEnumerator<T>
вместо IEnumerable<T>
ты можешь просто позвонить GetEnumerator()
var enumerator = Digits().OfType<int>().GetEnumerator();
Это работает для меня.
IEnumerator<DataColumn> columnEnumerator = dt.Columns.Cast<DataColumn>().GetEnumerator();