【问题标题】:Using a configurable enumerator使用可配置的枚举器
【发布时间】:2015-04-18 14:44:35
【问题描述】:

我有一个类,它封装了我们称之为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&lt;T&gt; 进行特定调用,实际上我是从这个开始的:

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 的实现细节意味着,仅返回一种类型的项目与拉出所有项目然后在框外排序之间存在很大差异。

【问题讨论】:

  • 在您的ItemCollection 中保存Item 或派生T 项目的底层存储容器是什么?
  • 一个链接列表和不安全代码的网络,将来自许多集合的项目混合在一起,旨在接近硬件 API。 ItemCollection 是一种弥合代码安全部分和不安全部分之间差距的方法。明智的实施更接近于说项目是按类型在内部分组而不是按集合,这就是为什么 ItemsOfType 更快(直接转到一组类型的项目,然后按关联的集合 id 过滤)然后很少使用的“全部”必须遍历内存中的每一项。我认为导致结构的丢弃结构可能是关键。

标签: c# generics compact-framework enumeration


【解决方案1】:

在你的回答之后,你让我有点困惑。我想我一直在想你的问题。

我现在明白了,您唯一需要的是实现GetEnumerator() 方法的结构,这样您就可以执行foreach 等。我没有意识到您已经完成了项目本身的IEnumerator&lt;&gt; 实现。

一个可能的改进是让您的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();
    }
}

这允许您从您的ItemsOfType&lt;T&gt;() 方法返回一个IEnumerable&lt;T&gt;,并隐藏您的实现。

包装器的替代方案是让您的ItemEnumerator&lt;T&gt; 结构实现IEnumerator&lt;T&gt;IEnumerable&lt;T&gt; 接口,并将它们设为私有。这只会在它们的实现中添加 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 接口。

【讨论】:

    【解决方案2】:

    好的,这是一个很好的例子,说明了说出来并休息一下。解决方案确实是导致结构枚举器的丢弃结构。我正在遵循一个非典型的实现,它让我纠结于存储值类型枚举器的值类型的气味以及发生坏事的可能性。退后一步,重新开始传统的类模式可以很容易地适应没有异味的结构。

    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 中使用不安全代码和垃圾收集器管理会让您开始过度思考问题。

    【讨论】:

      猜你喜欢
      • 2020-02-27
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-20
      相关资源
      最近更新 更多