[英]Generic type parameter as type parameter and new a generic type in one of the function of C#
我想实现一个优先级队列,可以通过它指定队列的类型(通用队列或唯一队列)。
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而不是从UniqueueQueue中调用了Enqueue()函数。 但是,当我调试时,我发现在PriorityQueue的Enqueue中,该变量是UniqueQueue的类型,但它没有调用UniqueQueue的Enqueue()。 我真的很想知道为什么,或者给我一些建议以解决这个问题?
它在粗体行中失败,并且堆栈跟踪为
Assert.AreEqual失败。 预期:。 实际:。 在ToolsTests.PriorityQueueTest.TestPriorityUniqueQueue()中的位置somefolder \\ ToolsTests \\ PriorityQueueTests.cs:行号118
通用类型参数TQueue<T>
的类型应为Queue<T>
因为您是在PriorityQueue
的通用约束中定义的:
where TQueue : Queue<TItem>, new ()
现在,只要您的代码通过:
_data.Add(key, new TQueue());
这将创建一个实例Queue<T>
而不是UniqueQueue<T>
因此它永远不会达到Enqueue
的方法UniqueQueue<T>
因为它调用了Enqueue
的方法Queue<T>
这样做的原因是由于您提供了一般约束(上面的第一个代码块)。 它根本不知道UniqueQueue<T>
类型,而是Queue<T>
类型,因为那是PriorityQueue<TItem, TKey, TQueue>
期望的(因为您这样“告诉”了他)。 因此,无论何时执行new TQueue()
,都会实例化一个新的Queue<T>
对象而不是UniqueQueue<T>
。
为了使测试通过,并且PriorityQueue<TItem, TKey, TQueue>
实际使用UniqueQueue<T>
,必须在通用约束中显式指定要使用UniqueQueue<T>
:
where TQueue : UniqueQueue<TItem>, new()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.