简体   繁体   English

通用类型参数作为类型参数,并在C#函数之一中新增了通用类型

[英]Generic type parameter as type parameter and new a generic type in one of the function of C#

I want to implement a Priority Queue, with the ablity can specify the type of the queue(generic queue or unique queue). 我想实现一个优先级队列,可以通过它指定队列的类型(通用队列或唯一队列)。

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;
    }
}

and the unique queue 和唯一的队列

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();
    }
}

But in the unit test, it fails with the following code 但是在单元测试中,它失败并显示以下代码

       [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);
    }

I debug the test case and find when call the puq.Enqueue(node1), it calls the Enqueue() function from Queue, not my UniqueueQueue. 我调试了测试用例,并发现在调用puq.Enqueue(node1)时,它从Queue而不是从UniqueueQueue中调用了Enqueue()函数。 But when I debug, I find that in the Enqueue of PriorityQueue, the variable is type of UniqueQueue, but it not call the Enqueue() of UniqueQueue. 但是,当我调试时,我发现在PriorityQueue的Enqueue中,该变量是UniqueQueue的类型,但它没有调用UniqueQueue的Enqueue()。 I really want to know why, or give me some advise about how this possible? 我真的很想知道为什么,或者给我一些建议以解决这个问题?

It fails in the bold line and the stack trace is 它在粗体行中失败,并且堆栈跟踪为

Assert.AreEqual failed. Assert.AreEqual失败。 Expected:. 预期:。 Actual:. 实际:。 in ToolsTests.PriorityQueueTest.TestPriorityUniqueQueue() position somefolder\\ToolsTests\\PriorityQueueTests.cs:line number 118 在ToolsTests.PriorityQueueTest.TestPriorityUniqueQueue()中的位置somefolder \\ ToolsTests \\ PriorityQueueTests.cs:行号118

the debug info of the queue variable from PriorityQueue<>.Enqueue() methord PriorityQueue <>。Enqueue()方法的队列变量的调试信息

Your generic type parameter TQueue<T> is expected to be of the type Queue<T> because you defined it in the generic constraint of the PriorityQueue : 通用类型参数TQueue<T>的类型应为Queue<T>因为您是在PriorityQueue的通用约束中定义的:

 where TQueue : Queue<TItem>, new ()

Now whenever your code passes: 现在,只要您的代码通过:

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

It will create an instance of Queue<T> and not UniqueQueue<T> , hence it will never reach the Enqueue method of your UniqueQueue<T> as it calls the Enqueue method of Queue<T> . 这将创建一个实例Queue<T>而不是UniqueQueue<T>因此它永远不会达到Enqueue的方法UniqueQueue<T>因为它调用了Enqueue的方法Queue<T>

Reasoning for this is because of the generic constraint (first code block above) you've supplied. 这样做的原因是由于您提供了一般约束(上面的第一个代码块)。 It simply does not know the UniqueQueue<T> type, but rather the Queue<T> type, since that is what the PriorityQueue<TItem, TKey, TQueue> expects (because you 'told' him so). 它根本不知道UniqueQueue<T>类型,而是Queue<T>类型,因为那是PriorityQueue<TItem, TKey, TQueue>期望的(因为您这样“告诉”了他)。 So whenever new TQueue() is executed, a new Queue<T> object will instantiated instead of UniqueQueue<T> . 因此,无论何时执行new TQueue() ,都会实例化一个新的Queue<T>对象而不是UniqueQueue<T>

In order for your tests to pass, and the PriorityQueue<TItem, TKey, TQueue> to actually use the UniqueQueue<T> , you must explicitly specify that you want to use the UniqueQueue<T> in the generic constraint: 为了使测试通过,并且PriorityQueue<TItem, TKey, TQueue>实际使用UniqueQueue<T> ,必须在通用约束中显式指定要使用UniqueQueue<T>

where TQueue : UniqueQueue<TItem>, new()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM