C# 中 OrderDictionary 的通用实现显示不明确的方法警告
Generic implementation of OrderDictionary in C# is showing ambiguous method warnings
由于在问这个问题时 C# 没有 OrderedDictionary 的通用实现,所以我从 here 下载了一个。非常清楚,我在 Unity 游戏引擎中使用它和 MonoDevelop 来编写游戏代码。
该实现似乎很好地组合在一起,但是它给了我一个模棱两可的方法调用,警告我似乎无法弄清楚的解决方案。有人可以向我解释这里发生了什么,并提出一个可能的解决方案来消除警告吗?
具体来说,这里是类似的方法调用:
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
这是错误:
[Warning] [CS0278] `TurboLabz.Game.IOrderedDictionary<string,TurboLabz.Game.RoomInfo>' contains ambiguous implementation of `enumerable' pattern.
Method `System.Collections.Specialized.IOrderedDictionary.GetEnumerator()' is ambiguous with method `System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,TurboLabz.Game.RoomInfo>>.GetEnumerator()'
提前致谢。
编辑:
这是源代码及其在我拥有的代码库中的用法:
IOrderedDictionary.cs
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public interface IOrderedDictionary<TKey, TValue> : IOrderedDictionary, IDictionary<TKey, TValue>
{
new int Add(TKey key, TValue value);
void Insert(int index, TKey key, TValue value);
new TValue this[int index]
{
get;
set;
}
}
}
OrderedDictionary.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private const int DefaultInitialCapacity = 0;
private static readonly string _keyTypeName = typeof(TKey).FullName;
private static readonly string _valueTypeName = typeof(TValue).FullName;
private static readonly bool _valueTypeIsReferenceType = !typeof(ValueType).IsAssignableFrom(typeof(TValue));
private Dictionary<TKey, TValue> _dictionary;
private List<KeyValuePair<TKey, TValue>> _list;
private IEqualityComparer<TKey> _comparer;
private object _syncRoot;
private int _initialCapacity;
public OrderedDictionary()
: this(DefaultInitialCapacity, null)
{
}
public OrderedDictionary(int capacity)
: this(capacity, null)
{
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
: this(DefaultInitialCapacity, comparer)
{
}
public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
if(0 > capacity)
throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative");
_initialCapacity = capacity;
_comparer = comparer;
}
private static TKey ConvertToKeyType(object keyObject)
{
if(null == keyObject)
{
throw new ArgumentNullException("key");
}
else
{
if(keyObject is TKey)
return (TKey)keyObject;
}
throw new ArgumentException("'key' must be of type " + _keyTypeName, "key");
}
private static TValue ConvertToValueType(object value)
{
if(null == value)
{
if(_valueTypeIsReferenceType)
return default(TValue);
else
throw new ArgumentNullException("value");
}
else
{
if(value is TValue)
return (TValue)value;
}
throw new ArgumentException("'value' must be of type " + _valueTypeName, "value");
}
private Dictionary<TKey, TValue> Dictionary
{
get
{
if(null == _dictionary)
{
_dictionary = new Dictionary<TKey, TValue>(_initialCapacity, _comparer);
}
return _dictionary;
}
}
private List<KeyValuePair<TKey, TValue>> List
{
get
{
if(null == _list)
{
_list = new List<KeyValuePair<TKey, TValue>>(_initialCapacity);
}
return _list;
}
}
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
public void Insert(int index, TKey key, TValue value)
{
if(index > Count || index < 0)
throw new ArgumentOutOfRangeException("index");
Dictionary.Add(key, value);
List.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
}
void IOrderedDictionary.Insert(int index, object key, object value)
{
Insert(index, ConvertToKeyType(key), ConvertToValueType(value));
}
public void RemoveAt(int index)
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List.RemoveAt(index);
Dictionary.Remove(key);
}
public TValue this[int index]
{
get
{
return List[index].Value;
}
set
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List[index] = new KeyValuePair<TKey, TValue>(key, value);
Dictionary[key] = value;
}
}
object IOrderedDictionary.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = ConvertToValueType(value);
}
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
Add(key, value);
}
public int Add(TKey key, TValue value)
{
Dictionary.Add(key, value);
List.Add(new KeyValuePair<TKey,TValue>(key, value));
return Count - 1;
}
void IDictionary.Add(object key, object value)
{
Add(ConvertToKeyType(key), ConvertToValueType(value));
}
public void Clear()
{
Dictionary.Clear();
List.Clear();
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
bool IDictionary.Contains(object key)
{
return ContainsKey(ConvertToKeyType(key));
}
bool IDictionary.IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
ICollection IDictionary.Keys
{
get
{
return (ICollection)Keys;
}
}
public int IndexOfKey(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
for(int index = 0; index < List.Count; index++)
{
KeyValuePair<TKey, TValue> entry = List[index];
TKey next = entry.Key;
if(null != _comparer)
{
if(_comparer.Equals(next, key))
{
return index;
}
}
else if(next.Equals(key))
{
return index;
}
}
return -1;
}
public bool Remove(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
int index = IndexOfKey(key);
if(index >= 0)
{
if(Dictionary.Remove(key))
{
List.RemoveAt(index);
return true;
}
}
return false;
}
void IDictionary.Remove(object key)
{
Remove(ConvertToKeyType(key));
}
ICollection IDictionary.Values
{
get
{
return (ICollection)Values;
}
}
public TValue this[TKey key]
{
get
{
return Dictionary[key];
}
set
{
if(Dictionary.ContainsKey(key))
{
Dictionary[key] = value;
List[IndexOfKey(key)] = new KeyValuePair<TKey, TValue>(key, value);
}
else
{
Add(key, value);
}
}
}
object IDictionary.this[object key]
{
get
{
return this[ConvertToKeyType(key)];
}
set
{
this[ConvertToKeyType(key)] = ConvertToValueType(value);
}
}
void ICollection.CopyTo(Array array, int index)
{
((ICollection)List).CopyTo(array, index);
}
public int Count
{
get
{
return List.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
get
{
if(this._syncRoot == null)
{
System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
}
return this._syncRoot;
}
}
public ICollection<TKey> Keys
{
get
{
return Dictionary.Keys;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get
{
return Dictionary.Values;
}
}
void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).Contains(item);
}
void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
}
}
这是上面给定的 OrderedDictionary 的使用方式:
IRoomSettingsModel.cs
namespace TurboLabz.Game
{
public interface IRoomSettingsModel
{
IOrderedDictionary<string, RoomInfo> settings { get; set; }
}
}
RoomSettingsModel.cs
namespace TurboLabz.Game
{
public class RoomSettingsModel : IRoomSettingsModel
{
public IOrderedDictionary<string, RoomInfo> settings { get; set; }
public RoomSettingsModel()
{
settings = new OrderedDictionary<string, RoomInfo>();
}
}
public struct RoomInfo
{
public string id;
public long gameDuration;
public long prize;
}
}
GSService.cs
namespace TurboLabz.Game
{
public class SomeService
{
public IRoomSettingsModel roomSettingsModel = new RoomSettingsModel();
public void ReadModel()
{
foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings)
{
RoomInfo roomInfo = room.Value;
Debug.Log(roomInfo.id);
}
}
}
}
为了保密,我在这里稍微更改了代码,但总的来说它应该传达了这个想法。上面用法中最重要的语句是 foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings)
,它是警告的来源。正是在这一行中,我认为编译器对调用哪个 GetEnumerator()
方法感到困惑。
首先,这真的是问题所在吗?其次,我该如何解决这个问题?
我试着按照你所做的去做,但这是一堆嵌套接口。
如果您在 OrderedDictionary
中的每个 GetEnumerator()
中放置断点,您可能会发现它没有调用您期望的枚举器。
我认为问题在于尝试实现非通用 IOrderedDictionary
接口以及 IDictionary<TKey, TValue>
。
如果你想要泛型,为什么你需要保持与非泛型的兼容性IOrderedDictionary
?
如果您按照 (F12) IOrderedDictionary
的继承轨迹,它将继承 IDictionary
、ICollection
、IEnumerable
.
那么IDictionary<TKey, TValue>
继承自ICollection<KeyValuePair<TKey, TValue>>
,IEnumerable<KeyValuePair<TKey, TValue>>
,IEnumerable
.
我不太确定您的所有要求是什么,但我会放弃您不需要支持的任何接口。不要提供您不需要的代码功能。
这不完全是您造成的,但它是尝试支持多个接口的结果,但它们本身有很多包袱。
根据你的问题,我只支持IDictionary<TKey, TValue>
& IList<T>
。
还有他们的行李 ;)
对于那些对 KeyedCollection 感到好奇的人,这里有一个实现了 @Mubeen 在他的代码中实现的大部分功能。这还没有完全测试,所以如果你使用它,不要只是复制->粘贴。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
namespace TurboLabz.Game
{
public class GenericComparer<TKey> : IComparer<TKey>
{
public static GenericComparer<TKey> CreateComparer(Func<TKey, TKey, int> comparer)
{
return new GenericComparer<TKey>(comparer);
}
internal GenericComparer(Func<TKey, TKey, int> comparer)
{
Comparer = comparer;
}
private Func<TKey, TKey, int> Comparer { get; set; }
public int Compare(TKey x, TKey y)
{
return Comparer(x, y);
}
}
public class OrderedDictionaryKC<TKey, TValue> : KeyedCollection<TKey,KeyValuePair<TKey, TValue>>
{
public OrderedDictionaryKC()
{ }
public OrderedDictionaryKC(IEnumerable<KeyValuePair<TKey, TValue>> collection)
{
if (collection != null)
{
foreach (KeyValuePair<TKey, TValue> item in collection)
{
base.Add(item);
}
}
}
public OrderedDictionaryKC(IDictionary<TKey, TValue> dictionary) : this((IEnumerable<KeyValuePair<TKey, TValue>>)dictionary)
{ }
public ICollection<TKey> Keys
{
get
{
return base.Dictionary.Keys;
}
}
public ICollection<KeyValuePair<TKey, TValue>> Values
{
get
{
return base.Dictionary.Values;
}
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
base.Add(new KeyValuePair<TKey, TValue>(key, value));
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return base.Dictionary.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
KeyValuePair<TKey, TValue> outValue;
var result= base.Dictionary.TryGetValue(key, out outValue);
value = outValue.Value;
return result;
}
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
}
}
我最终编写了一个新的实现,它是围绕 System.Collections.Specialized.OrderedDictionary
的纯通用包装器。
虽然这不是对原始问题的回答,但它没有警告,并且像他的回答中提到的@ashley-pillay 一样,只实现了必要的接口。
我在这里提供实现,希望能帮助其他人,因为即使经过大量谷歌搜索,也很难找到无警告泛型的良好实现 OrderedDictionary
。
IOrderedDictionary.cs
using System.Collections.Generic;
namespace TurboLabz.Common
{
public interface IOrderedDictionary<TKey, TValue> :
IDictionary<TKey, TValue>,
ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>
{
}
}
OrderedDictionary.cs
//-----------------------------------------------------------------------------
// Initial code provided by Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Common
{
// System.Collections.Specialized.OrderedDictionary is NOT generic.
// This class is essentially a generic wrapper for OrderedDictionary.
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private OrderedDictionary _internalDictionary;
public OrderedDictionary()
{
_internalDictionary = new OrderedDictionary();
}
public OrderedDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary != null)
{
_internalDictionary = new OrderedDictionary();
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
_internalDictionary.Add(pair.Key, pair.Value);
}
}
}
public int Count
{
get
{
return _internalDictionary.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public TValue this[TKey key]
{
get
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
return (TValue)_internalDictionary[(object)key];
}
else
{
throw new KeyNotFoundException("Cannot find key " + key);
}
}
set
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary[(object)key] = value;
}
}
public ICollection<TKey> Keys
{
get
{
IList<TKey> keys = new List<TKey>(_internalDictionary.Count);
foreach (TKey key in _internalDictionary.Keys)
{
keys.Add(key);
}
// Keys should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return keys;
}
}
public ICollection<TValue> Values
{
get
{
IList<TValue> values = new List<TValue>(_internalDictionary.Count);
foreach (TValue value in _internalDictionary.Values)
{
values.Add(value);
}
// Values should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return values;
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary.Add(key, value);
}
public void Clear()
{
_internalDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
if ((item.Key == null) || !(_internalDictionary.Contains(item.Key)))
{
return false;
}
else
{
return _internalDictionary[(object)item.Key].Equals(item.Value);
}
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return _internalDictionary.Contains(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
if ((array.Rank > 1) ||
(arrayIndex >= array.Length) ||
((array.Length - arrayIndex) < _internalDictionary.Count))
{
throw new Exception("Fx.Exception.Argument('array', SRCore.BadCopyToArray)");
}
int index = arrayIndex;
foreach (DictionaryEntry entry in _internalDictionary)
{
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
++index;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in _internalDictionary)
{
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (Contains(item))
{
_internalDictionary.Remove(item.Key);
return true;
}
else
{
return false;
}
}
public bool Remove(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
_internalDictionary.Remove(key);
return true;
}
else
{
return false;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
bool keyExists = _internalDictionary.Contains(key);
value = keyExists ? (TValue)_internalDictionary[(object)key] : default(TValue);
return keyExists;
}
}
}
由于在问这个问题时 C# 没有 OrderedDictionary 的通用实现,所以我从 here 下载了一个。非常清楚,我在 Unity 游戏引擎中使用它和 MonoDevelop 来编写游戏代码。
该实现似乎很好地组合在一起,但是它给了我一个模棱两可的方法调用,警告我似乎无法弄清楚的解决方案。有人可以向我解释这里发生了什么,并提出一个可能的解决方案来消除警告吗?
具体来说,这里是类似的方法调用:
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
这是错误:
[Warning] [CS0278] `TurboLabz.Game.IOrderedDictionary<string,TurboLabz.Game.RoomInfo>' contains ambiguous implementation of `enumerable' pattern.
Method `System.Collections.Specialized.IOrderedDictionary.GetEnumerator()' is ambiguous with method `System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,TurboLabz.Game.RoomInfo>>.GetEnumerator()'
提前致谢。
编辑:
这是源代码及其在我拥有的代码库中的用法:
IOrderedDictionary.cs
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public interface IOrderedDictionary<TKey, TValue> : IOrderedDictionary, IDictionary<TKey, TValue>
{
new int Add(TKey key, TValue value);
void Insert(int index, TKey key, TValue value);
new TValue this[int index]
{
get;
set;
}
}
}
OrderedDictionary.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Game
{
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private const int DefaultInitialCapacity = 0;
private static readonly string _keyTypeName = typeof(TKey).FullName;
private static readonly string _valueTypeName = typeof(TValue).FullName;
private static readonly bool _valueTypeIsReferenceType = !typeof(ValueType).IsAssignableFrom(typeof(TValue));
private Dictionary<TKey, TValue> _dictionary;
private List<KeyValuePair<TKey, TValue>> _list;
private IEqualityComparer<TKey> _comparer;
private object _syncRoot;
private int _initialCapacity;
public OrderedDictionary()
: this(DefaultInitialCapacity, null)
{
}
public OrderedDictionary(int capacity)
: this(capacity, null)
{
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
: this(DefaultInitialCapacity, comparer)
{
}
public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
if(0 > capacity)
throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative");
_initialCapacity = capacity;
_comparer = comparer;
}
private static TKey ConvertToKeyType(object keyObject)
{
if(null == keyObject)
{
throw new ArgumentNullException("key");
}
else
{
if(keyObject is TKey)
return (TKey)keyObject;
}
throw new ArgumentException("'key' must be of type " + _keyTypeName, "key");
}
private static TValue ConvertToValueType(object value)
{
if(null == value)
{
if(_valueTypeIsReferenceType)
return default(TValue);
else
throw new ArgumentNullException("value");
}
else
{
if(value is TValue)
return (TValue)value;
}
throw new ArgumentException("'value' must be of type " + _valueTypeName, "value");
}
private Dictionary<TKey, TValue> Dictionary
{
get
{
if(null == _dictionary)
{
_dictionary = new Dictionary<TKey, TValue>(_initialCapacity, _comparer);
}
return _dictionary;
}
}
private List<KeyValuePair<TKey, TValue>> List
{
get
{
if(null == _list)
{
_list = new List<KeyValuePair<TKey, TValue>>(_initialCapacity);
}
return _list;
}
}
IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
{
return List.GetEnumerator();
}
public void Insert(int index, TKey key, TValue value)
{
if(index > Count || index < 0)
throw new ArgumentOutOfRangeException("index");
Dictionary.Add(key, value);
List.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
}
void IOrderedDictionary.Insert(int index, object key, object value)
{
Insert(index, ConvertToKeyType(key), ConvertToValueType(value));
}
public void RemoveAt(int index)
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List.RemoveAt(index);
Dictionary.Remove(key);
}
public TValue this[int index]
{
get
{
return List[index].Value;
}
set
{
if(index >= Count || index < 0)
throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
TKey key = List[index].Key;
List[index] = new KeyValuePair<TKey, TValue>(key, value);
Dictionary[key] = value;
}
}
object IOrderedDictionary.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = ConvertToValueType(value);
}
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
Add(key, value);
}
public int Add(TKey key, TValue value)
{
Dictionary.Add(key, value);
List.Add(new KeyValuePair<TKey,TValue>(key, value));
return Count - 1;
}
void IDictionary.Add(object key, object value)
{
Add(ConvertToKeyType(key), ConvertToValueType(value));
}
public void Clear()
{
Dictionary.Clear();
List.Clear();
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
bool IDictionary.Contains(object key)
{
return ContainsKey(ConvertToKeyType(key));
}
bool IDictionary.IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
ICollection IDictionary.Keys
{
get
{
return (ICollection)Keys;
}
}
public int IndexOfKey(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
for(int index = 0; index < List.Count; index++)
{
KeyValuePair<TKey, TValue> entry = List[index];
TKey next = entry.Key;
if(null != _comparer)
{
if(_comparer.Equals(next, key))
{
return index;
}
}
else if(next.Equals(key))
{
return index;
}
}
return -1;
}
public bool Remove(TKey key)
{
if(null == key)
throw new ArgumentNullException("key");
int index = IndexOfKey(key);
if(index >= 0)
{
if(Dictionary.Remove(key))
{
List.RemoveAt(index);
return true;
}
}
return false;
}
void IDictionary.Remove(object key)
{
Remove(ConvertToKeyType(key));
}
ICollection IDictionary.Values
{
get
{
return (ICollection)Values;
}
}
public TValue this[TKey key]
{
get
{
return Dictionary[key];
}
set
{
if(Dictionary.ContainsKey(key))
{
Dictionary[key] = value;
List[IndexOfKey(key)] = new KeyValuePair<TKey, TValue>(key, value);
}
else
{
Add(key, value);
}
}
}
object IDictionary.this[object key]
{
get
{
return this[ConvertToKeyType(key)];
}
set
{
this[ConvertToKeyType(key)] = ConvertToValueType(value);
}
}
void ICollection.CopyTo(Array array, int index)
{
((ICollection)List).CopyTo(array, index);
}
public int Count
{
get
{
return List.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
get
{
if(this._syncRoot == null)
{
System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
}
return this._syncRoot;
}
}
public ICollection<TKey> Keys
{
get
{
return Dictionary.Keys;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get
{
return Dictionary.Values;
}
}
void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).Contains(item);
}
void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey,TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
}
}
这是上面给定的 OrderedDictionary 的使用方式:
IRoomSettingsModel.cs
namespace TurboLabz.Game
{
public interface IRoomSettingsModel
{
IOrderedDictionary<string, RoomInfo> settings { get; set; }
}
}
RoomSettingsModel.cs
namespace TurboLabz.Game
{
public class RoomSettingsModel : IRoomSettingsModel
{
public IOrderedDictionary<string, RoomInfo> settings { get; set; }
public RoomSettingsModel()
{
settings = new OrderedDictionary<string, RoomInfo>();
}
}
public struct RoomInfo
{
public string id;
public long gameDuration;
public long prize;
}
}
GSService.cs
namespace TurboLabz.Game
{
public class SomeService
{
public IRoomSettingsModel roomSettingsModel = new RoomSettingsModel();
public void ReadModel()
{
foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings)
{
RoomInfo roomInfo = room.Value;
Debug.Log(roomInfo.id);
}
}
}
}
为了保密,我在这里稍微更改了代码,但总的来说它应该传达了这个想法。上面用法中最重要的语句是 foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings)
,它是警告的来源。正是在这一行中,我认为编译器对调用哪个 GetEnumerator()
方法感到困惑。
首先,这真的是问题所在吗?其次,我该如何解决这个问题?
我试着按照你所做的去做,但这是一堆嵌套接口。
如果您在 OrderedDictionary
中的每个 GetEnumerator()
中放置断点,您可能会发现它没有调用您期望的枚举器。
我认为问题在于尝试实现非通用 IOrderedDictionary
接口以及 IDictionary<TKey, TValue>
。
如果你想要泛型,为什么你需要保持与非泛型的兼容性IOrderedDictionary
?
如果您按照 (F12) IOrderedDictionary
的继承轨迹,它将继承 IDictionary
、ICollection
、IEnumerable
.
那么IDictionary<TKey, TValue>
继承自ICollection<KeyValuePair<TKey, TValue>>
,IEnumerable<KeyValuePair<TKey, TValue>>
,IEnumerable
.
我不太确定您的所有要求是什么,但我会放弃您不需要支持的任何接口。不要提供您不需要的代码功能。
这不完全是您造成的,但它是尝试支持多个接口的结果,但它们本身有很多包袱。
根据你的问题,我只支持IDictionary<TKey, TValue>
& IList<T>
。
还有他们的行李 ;)
对于那些对 KeyedCollection 感到好奇的人,这里有一个实现了 @Mubeen 在他的代码中实现的大部分功能。这还没有完全测试,所以如果你使用它,不要只是复制->粘贴。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
namespace TurboLabz.Game
{
public class GenericComparer<TKey> : IComparer<TKey>
{
public static GenericComparer<TKey> CreateComparer(Func<TKey, TKey, int> comparer)
{
return new GenericComparer<TKey>(comparer);
}
internal GenericComparer(Func<TKey, TKey, int> comparer)
{
Comparer = comparer;
}
private Func<TKey, TKey, int> Comparer { get; set; }
public int Compare(TKey x, TKey y)
{
return Comparer(x, y);
}
}
public class OrderedDictionaryKC<TKey, TValue> : KeyedCollection<TKey,KeyValuePair<TKey, TValue>>
{
public OrderedDictionaryKC()
{ }
public OrderedDictionaryKC(IEnumerable<KeyValuePair<TKey, TValue>> collection)
{
if (collection != null)
{
foreach (KeyValuePair<TKey, TValue> item in collection)
{
base.Add(item);
}
}
}
public OrderedDictionaryKC(IDictionary<TKey, TValue> dictionary) : this((IEnumerable<KeyValuePair<TKey, TValue>>)dictionary)
{ }
public ICollection<TKey> Keys
{
get
{
return base.Dictionary.Keys;
}
}
public ICollection<KeyValuePair<TKey, TValue>> Values
{
get
{
return base.Dictionary.Values;
}
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
base.Add(new KeyValuePair<TKey, TValue>(key, value));
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return base.Dictionary.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
KeyValuePair<TKey, TValue> outValue;
var result= base.Dictionary.TryGetValue(key, out outValue);
value = outValue.Value;
return result;
}
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
}
}
我最终编写了一个新的实现,它是围绕 System.Collections.Specialized.OrderedDictionary
的纯通用包装器。
虽然这不是对原始问题的回答,但它没有警告,并且像他的回答中提到的@ashley-pillay 一样,只实现了必要的接口。
我在这里提供实现,希望能帮助其他人,因为即使经过大量谷歌搜索,也很难找到无警告泛型的良好实现 OrderedDictionary
。
IOrderedDictionary.cs
using System.Collections.Generic;
namespace TurboLabz.Common
{
public interface IOrderedDictionary<TKey, TValue> :
IDictionary<TKey, TValue>,
ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>
{
}
}
OrderedDictionary.cs
//-----------------------------------------------------------------------------
// Initial code provided by Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace TurboLabz.Common
{
// System.Collections.Specialized.OrderedDictionary is NOT generic.
// This class is essentially a generic wrapper for OrderedDictionary.
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
{
private OrderedDictionary _internalDictionary;
public OrderedDictionary()
{
_internalDictionary = new OrderedDictionary();
}
public OrderedDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary != null)
{
_internalDictionary = new OrderedDictionary();
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
_internalDictionary.Add(pair.Key, pair.Value);
}
}
}
public int Count
{
get
{
return _internalDictionary.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public TValue this[TKey key]
{
get
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
return (TValue)_internalDictionary[(object)key];
}
else
{
throw new KeyNotFoundException("Cannot find key " + key);
}
}
set
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary[(object)key] = value;
}
}
public ICollection<TKey> Keys
{
get
{
IList<TKey> keys = new List<TKey>(_internalDictionary.Count);
foreach (TKey key in _internalDictionary.Keys)
{
keys.Add(key);
}
// Keys should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return keys;
}
}
public ICollection<TValue> Values
{
get
{
IList<TValue> values = new List<TValue>(_internalDictionary.Count);
foreach (TValue value in _internalDictionary.Values)
{
values.Add(value);
}
// Values should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return values;
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Add(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_internalDictionary.Add(key, value);
}
public void Clear()
{
_internalDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
if ((item.Key == null) || !(_internalDictionary.Contains(item.Key)))
{
return false;
}
else
{
return _internalDictionary[(object)item.Key].Equals(item.Value);
}
}
public bool ContainsKey(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
return _internalDictionary.Contains(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
if ((array.Rank > 1) ||
(arrayIndex >= array.Length) ||
((array.Length - arrayIndex) < _internalDictionary.Count))
{
throw new Exception("Fx.Exception.Argument('array', SRCore.BadCopyToArray)");
}
int index = arrayIndex;
foreach (DictionaryEntry entry in _internalDictionary)
{
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
++index;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in _internalDictionary)
{
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (Contains(item))
{
_internalDictionary.Remove(item.Key);
return true;
}
else
{
return false;
}
}
public bool Remove(TKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (_internalDictionary.Contains(key))
{
_internalDictionary.Remove(key);
return true;
}
else
{
return false;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
bool keyExists = _internalDictionary.Contains(key);
value = keyExists ? (TValue)_internalDictionary[(object)key] : default(TValue);
return keyExists;
}
}
}