C#中OrderDictionary的通用实现显示模糊方法警告

时间:2017-07-31 12:47:39

标签: c# generics unity3d compiler-warnings ordereddictionary

由于C#在提出这个问题时没有OrderedDictionary的通用实现,我从here下载了一个。非常清楚我在使用MonoDevelop的Unity游戏引擎中使用它来编写游戏代码。

实现似乎很好地组合在一起然而它给了我一个模糊的方法调用警告解决方案,我似乎无法弄清楚。有人可以解释一下这里发生了什么,并提出一个可能的解决办法来摆脱警告吗?

这里具体的是类似的方法调用:

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()方法感到困惑。

首先,这真的是问题吗?其次,我该如何解决这个问题?

2 个答案:

答案 0 :(得分:3)

我试着按照你所做的去做,但它是嵌套接口的意大利面。

如果您在GetEnumerator()中的每个OrderedDictionary中放置断点,您可能会发现它没有调用您期望的枚举器。

我认为,问题在于尝试与IOrderedDictionary一起实施非通用IDictionary<TKey, TValue>界面。

如果你想要泛型,你为什么需要保持与非泛型IOrderedDictionary的兼容性?

如果您遵循(F12)IOrderedDictionary的继承跟踪,它会继承IDictionaryICollectionIEnumerable

然后IDictionary<TKey, TValue>继承自ICollection<KeyValuePair<TKey, TValue>>IEnumerable<KeyValuePair<TKey, TValue>>IEnumerable

我不太确定您的所有要求是什么,但我会丢弃您不需要支持的任何接口。不要提供您不需要的代码功能。

这不完全取决于你,但它是试图支持多个接口的结果,这些接口有很多自己的包袱。

根据您的问题,我只会支持IDictionary<TKey, TValue>&amp; 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;
        }
    }
}

答案 1 :(得分:1)

我最终编写了一个新的实现,它是围绕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;
        }
    }
}