Я бы не советовал заключать IEnumerable в итератор, чтобы получатели не обращали внимания на основное соединение. Я бы хотел использовать обертку примерно так:
public struct WrappedEnumerable<T> : IEnumerable<T>
{
IEnumerable<T> _dataSource;
public WrappedEnumerable(IEnumerable<T> dataSource)
{
_dataSource = dataSource;
}
public IEnumerator<T> GetEnumerator()
{
return _dataSource.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)_dataSource).GetEnumerator();
}
}
Если возвращаемый тип свойств равен IEnumerable<T>
, приведение типа от WrappedEnumerable<T>
до IEnumerable<T>
приведет к упорядочиванию структуры и приведению поведения и производительности в соответствие с классом. Однако если бы свойства были определены как возвращающий тип WrappedEnumerable<T>
, то можно было бы сохранить шаг упаковки в тех случаях, когда вызывающий код либо назначает возврат свойству типа WrappedEnumerable<T>
(скорее всего, в результате что-то вроде var myKeys = myCollection.Keys;
) или просто использует свойство непосредственно в цикле "foreach". Обратите внимание, что если перечислитель, возвращаемый GetEnumerator()
, будет структурой, он все равно должен быть заключен в квадрат в любом случае.
Преимущество в производительности при использовании структуры, а не класса, как правило, будет довольно незначительным; однако концептуально использование структуры соответствовало бы общей рекомендации, согласно которой свойства не создают новые экземпляры объектов кучи. Создание нового экземпляра структуры, который содержит только ссылку на существующий объект кучи, очень дешево. Самый большой недостаток использования структуры, как определено здесь, состоит в том, что она блокирует поведение вещи, возвращаемой в вызывающий код, тогда как простой возврат IEnumerable<T>
допускает другие подходы.
Также обратите внимание, что в некоторых случаях возможно исключить требование для любого бокса и использовать оптимизацию при наборе текста в C # и vb.net foreach
, если использовать такой тип, как:
public struct FancyWrappedEnumerable<TItems,TEnumerator,TDataSource> : IEnumerable<TItems> where TEnumerator : IEnumerator<TItems>
{
TDataSource _dataSource;
Func<TDataSource,TEnumerator> _convertor;
public FancyWrappedEnumerable(TDataSource dataSource, Func<TDataSource, TEnumerator> convertor)
{
_dataSource = dataSource;
_convertor = convertor;
}
public TEnumerator GetEnumerator()
{
return _convertor(_dataSource);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _convertor(_dataSource);
}
}
Делегат convertor
может быть статическим делегатом и, следовательно, не требует создания объектов кучи во время выполнения (вне инициализации класса). Используя этот подход, если кто-то хочет вернуть перечислитель из List<int>
, тип возвращаемого свойства будет FancyWrappedEnumerable<int, List<int>.Enumerator, List>
. Возможно, разумно, если вызывающая сторона просто использовала свойство непосредственно в цикле foreach
или в объявлении var
, но довольно странно, если вызывающая сторона хотела объявить место хранения типа способом, который не может использовать var
.