【问题标题】:Generic type parameter as type parameter and new a generic type in one of the function of C#泛型类型参数作为类型参数并在 C# 的功能之一中新建泛型类型
【发布时间】:2018-11-09 21:03:03
【问题描述】:

我想实现一个优先队列,可以指定队列的类型(通用队列或唯一队列)。

public class PriorityQueue<TItem, TKey, TQueue> : IEnumerable<TItem>
    where TKey : IComparable
    where TQueue : Queue<TItem>, new ()
{
    private readonly Func<TItem, TKey> _keySelector;

    private readonly SortedDictionary<TKey, TQueue> _data = new SortedDictionary<TKey, TQueue>();

    public PriorityQueue(Func<TItem, TKey> keySelector)
    {
        _keySelector = keySelector;
    }

    public void Enqueue(TItem item)
    {
        var key = _keySelector(item);

        if (!_data.ContainsKey(key))
        {
            _data.Add(key, new TQueue());
        }

        var queue = _data[key];
        queue.Enqueue(item);
    }

    public TItem Dequeue()
    {
        if (IsEmpty)
        {
            throw new ArgumentException("Queue is EMPTY");
        }

        var key = _data.Keys.First();
        var queue = _data[key];

        var item = queue.Dequeue();
        if (queue.Count == 0)
        {
            _data.Remove(key);
        }

        return item;
    }

    public bool IsEmpty => _data.Count == 0;

    private int Count
    {
        get
        {
            var count = 0;
            foreach (var key in _data.Keys)
            {
                count += _data[key].Count;
            }

            return count;
        }
    }

    public void Clear()
    {
        _data.Clear();
    }


    public IEnumerator<TItem> GetEnumerator()
    {
        return new Enumerator(_data);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    private class Enumerator : IEnumerator<TItem>
    {
        private readonly SortedDictionary<TKey, TQueue> _dictionary;
        private readonly int _dictionaryCount;
        private int _currentPosition;

        public Enumerator(SortedDictionary<TKey, TQueue> data)
        {
            _dictionary = data;
            _dictionaryCount = DictionaryCount;
            _currentPosition = 0;
            Current = default(TItem);
        }

        private int DictionaryCount
        {
            get
            {
                var count = 0;
                foreach (var key in _dictionary.Keys)
                {
                    count += _dictionary[key].Count;
                }

                return count;
            }
        }

        public void Dispose()
        {

        }

        public bool MoveNext()
        {
            if (_currentPosition >= _dictionaryCount)
            {
                return false;
            }

            Current = GetCurrent();
            _currentPosition++;

            return true;
        }

        public void Reset()
        {
            _currentPosition = 0;
        }

        public TItem Current { get; private set; }

        private TItem GetCurrent()
        {
            var sum = 0;
            var item = default(TItem);

            foreach (var key in _dictionary.Keys)
            {
                var queue = _dictionary[key];

                sum += queue.Count;

                if (sum > _currentPosition)
                {
                    item = queue.Take(queue.Count - (sum - _currentPosition) + 1).Last();
                    break;
                }
            }

            return item;
        }

        object IEnumerator.Current => Current;
    }
}

和唯一的队列

public class UniqueQueue<T> : Queue<T>
{
    private HashSet<T> _hashSet;

    public UniqueQueue()
    {
        _hashSet = new HashSet<T>();
    }

    public new void Enqueue(T item)
    {
        if (_hashSet.Add(item))
        {
            base.Enqueue(item);
        }
    }

    public new T Dequeue()
    {
        var item = base.Dequeue();
        _hashSet.Remove(item);
        return item;
    }

    public new void Clear()
    {
        _hashSet.Clear();
        base.Clear();
    }
}

但在单元测试中,它失败并显示以下代码

       [TestMethod]
    public void TestPriorityUniqueQueue()
    {
        var puq = new PriorityQueue<Node, int, UniqueQueue<Node>>(node => node.Key);

        var node1 = new Node(1, "One");
        var node2 = new Node(2, "Two");
        var node3 = new Node(1, "One 1");

        puq.Enqueue(node1);
        puq.Enqueue(node1);
        puq.Enqueue(node1);
        puq.Enqueue(node2);
        puq.Enqueue(node3);

        var list = new List<Node>();
        foreach (var node in puq)
        {
            list.Add(node);
        }

        Assert.AreEqual(list[0], node1);
        **Assert.AreEqual(list[1], node3);**
        Assert.AreEqual(list[2], node2);

        puq.Dequeue();
        puq.Dequeue();
        puq.Dequeue();

        Assert.IsTrue(puq.IsEmpty);
    }

我调试了测试用例,发现当调用puq.Enqueue(node1)时,它从Queue中调用Enqueue()函数,而不是我的UniqueueQueue。但是我调试的时候发现在PriorityQueue的Enqueue中,变量是UniqueQueue的类型,却没有调用UniqueQueue的Enqueue()。我真的很想知道为什么,或者给我一些关于这怎么可能的建议?

它在粗体行中失败,堆栈跟踪是

Assert.AreEqual 失败。预期的:。实际的:。 在 ToolsTests.PriorityQueueTest.TestPriorityUniqueQueue() 位置 somefolder\ToolsTests\PriorityQueueTests.cs:line number 118

the debug info of the queue variable from PriorityQueue<>.Enqueue() methord

【问题讨论】:

  • 欢迎来到 Stack Overflow。您能否提供失败的行和堆栈跟踪以及错误消息。它将帮助人们了解您的问题。
  • 感谢 Dragonthoughts,它在第三部分代码中失败,行 Assert.AreEqual(list[1], node3);并且堆栈跟踪没有任何帮助
  • 谢谢 dymanoid,我知道隐藏规则了。我很困惑,当我在 PriorityQueue 的 Enqueue() 函数中新建 TQueue 时,TQueue 应该是 UniqueueQueue,而不是 Queue,它应该调用 UniqueueQueue 的 Enqueue()。所以我认为问题出在我不知道的泛型类型参数上。

标签: c# generics types parameters


【解决方案1】:

您的泛型类型参数TQueue&lt;T&gt; 应该是Queue&lt;T&gt; 类型,因为您在PriorityQueue 的泛型约束中定义了它:

 where TQueue : Queue<TItem>, new ()

现在只要您的代码通过:

_data.Add(key, new TQueue());

它将创建Queue&lt;T&gt; 的实例而不是UniqueQueue&lt;T&gt;,因此它永远不会到达UniqueQueue&lt;T&gt;Enqueue 方法,因为它会调用Queue&lt;T&gt;Enqueue 方法。

这是因为您提供的通用约束(上面的第一个代码块)。它根本不知道UniqueQueue&lt;T&gt; 类型,而是Queue&lt;T&gt; 类型,因为这是PriorityQueue&lt;TItem, TKey, TQueue&gt; 所期望的(因为你'告诉'他这样)。因此,每当执行new TQueue() 时,都会实例化一个新的Queue&lt;T&gt; 对象而不是UniqueQueue&lt;T&gt;

为了让您的测试通过,并让PriorityQueue&lt;TItem, TKey, TQueue&gt; 实际使用UniqueQueue&lt;T&gt;,您必须明确指定要在通用约束中使用UniqueQueue&lt;T&gt;

where TQueue : UniqueQueue<TItem>, new()

【讨论】:

  • 哦,我明白了。我注意调试信息,当我使用 Debug.WriteLine(queue.GetType()); 在 PriorityQueue.Enqueue() 方法中打印队列变量时,它显示 CRSCD.ITP.Tools.UniqueQueue1[CRSCD.ITP.ToolsTests.Node]`,但是声明队列变量应该是 Queue,所以它调用 Queue.Enqueue()。非常感谢 Jevgeni。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
相关资源
最近更新 更多