我有一个类,它封装了我们称之为ItemCollection
的项目集合。我需要公开两个不同的枚举器来处理这个集合:一个简单枚举所有项(Item
),第二个只列举那些特定派生类型(T where T:Item
)。一个皱纹是,在紧凑框架上运行的一些实时库代码的这一部分,它确实需要避免在堆上分配可能在foreach循环中间触发集合的对象。我一直在使用struct Enumerators来填补这个要求。
public class ItemCollection
{
public ItemEnumerator GetEnumerator() {}
public ItemGenericEnumerator<T> GetEnumerator<T>() {} // Probably not correct
}
public struct ItemEnumerator : IEnumerator<Item>
{
Item Current;
bool MoveNext() {}
void Reset() {}
}
public struct ItemGenericEnumerator<T> : IEnumerator<T> where T : Item
{
T Current;
bool MoveNext() {}
void Reset() {}
}
从表面上看,我不知道如何打电话给GetEnumerator<T>
,事实上我从这开始:
public class ItemCollection
{
public ItemEnumerator GetEnumerator() {}
public ItemGenericEnumerator<T> ItemsOfType<T>() {}
}
foreach(var item in collection.ItemsOfType<X>)
但我立即遇到does not contain a public definition for 'GetEnumerator'
。
一个解决方案是让ItemsOfType返回一个普通的抛弃类,其中包含一个接收枚举器和单个GetEnumerator方法的构造函数,但该实现会破坏无堆分配要求(抛弃返回另一个带GetEnumerator结构的结构吗? )。
另一个被丢弃的选项是简单地使用第一个枚举器并要求手动检查类型,它只需要一行额外的代码。然而,ItemCollection的当前Implimentation细节意味着只返回一种类型的项目与拉动所有项目然后在框外排序它们之间存在很大差异。
答案 0 :(得分:1)
在你回答之后,你让我感到困惑。我想我一直在思考你的问题。
我现在明白,你唯一需要的是一个实现GetEnumerator()
方法的结构,所以你可以做foreach
等等。我没有意识到你已经有了IEnumerator<>
个实现对于物品本身已经完成。
可能的改进是让您的ItemEnumeratorWrapper
实现完整的IEnumerable
接口,这样您就可以将其设为私有内部结构,如:
private struct ItemEnumeratorWrapper<T> : IEnumerable<T> where T : Item
{
private ItemCollection _collection;
public ItemEnumeratorWrapper<T>(ItemCollection collection)
{
_collection = collection;
}
public ItemEnumerator<T> GetEnumerator()
{
return _collection.GetItemsOfTypeEnumerator<T>();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
}
这允许您从IEnumerable<T>
方法返回ItemsOfType<T>()
,并隐藏您的实施。
包装器的替代方法是让ItemEnumerator<T>
结构实现IEnumerator<T>
和IEnumerable<T>
接口,并将它们设为私有。这将仅为其实现添加2个非常小的附加方法,并且不需要中间包装器。
以下示例有点做作,因为我对您容器的内部结构一无所知,所以我做了一些事情来说明这个想法。
private struct ItemEnumerator<T> : IEnumerator<T>, IEnumerable<T> where T : Item
{
// Specific types for your unsafe internals
// completely made up.
private readonly SomeTypeOfCollectionHandle _handle;
private SomeTypeOfCursor _cursor = SomeTypeOfCursor.BeforeAll;
public ItemEnumerator(SomeTypeOfCollectionHandle handle)
{
_handle = handle;
}
// IEnumerable<T> implementation.
public IEnumerator<T> GetEnumerator()
{
// simply return a new instance with the same collection handle.
return new ItemEnumerator(_handle);
}
public bool MoveNext()
{
return (cursor = _handle.CursorNext(_cursor)).IsValid();
}
public T Current
{
get
{
if (!_cursor.IsValid())
throw new InvalidOperationException();
return _cursor.Read<T>();
}
}
object System.Collections.IEnumerator.Current
{
get { return (object)((IEnumerator<T>)this).Current; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
public void Dispose()
{
_cursor = _handle.CloseCursor(_cursor);
}
}
执行此操作将确保ItemCollection
的公共接口仅公开标准BCL接口。
答案 1 :(得分:0)
好的,所以这是一个很好的例子,说出来并休息一下。解决方案确实是一个抛弃结构导致结构枚举器。我正在遵循一个非典型的实现,让我纠结于存储值类型枚举器的值类型的气味以及可能发生的坏事。退回并开始新鲜的传统类图案可以很容易地适应没有气味的结构。
public class ItemCollection
{
private ItemEnumerator<T> GetItemsOfTypeEnumerator<T>() {}
public ItemEnumeratorWrapper<T> ItemsOfType<T>()
{
return new ItemEnumeratorWrapper<T>(this);
}
public ItemEnumerator<Item> GetEmumerator() {}
public struct ItemEnumeratorWrapper<T> where T : Item
{
private ItemCollection _collection;
public ItemEnumeratorWrapper<T>(ItemCollection collection)
{
_collection = collection;
}
public ItemEnumerator<T> GetEnumerator()
{
return _collection.GetItemsOfTypeEnumerator<T>();
}
}
}
在.NET中使用不安全的代码和垃圾收集器管理会让你开始彻底地过度思考。