I am trying to create a custom ReadOnlyDictionary<TKey, TValue> for .NET 4.0. The approach is to keep a private Dictionary<TKey, TValue> object as well as flags to determine whether adding/removing and item assignment is allowed.
This works fine but I wanted to implement the IDictionary<TKey, TValue> interface for completeness. However, I notice that it extends ICollection<KeyValuePair<TKey, TValue>> whereas none of its properties or methods appear in the Dictionary<TKey, TValue> class. How is this possible? If the interface is implemented, why are ICollection members not exposed?
Furthermore, why does the Dictionary class need to implement ICollection in the first place?
Here is a rough implementation:
public sealed class ReadOnlyDictionary<TKey, TValue>:
//IDictionary<TKey, TValue>,
IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Members.
public bool AllowListEdit { get; private set; }
public bool AllowItemEdit { get; private set; }
private Dictionary<TKey, TValue> Dictionary { get; set; }
#endregion Members.
#region Constructors.
public ReadOnlyDictionary (bool allowListEdit, bool allowItemEdit) { this.AllowListEdit = allowListEdit; this.AllowItemEdit = allowItemEdit; this.Dictionary = new Dictionary<TKey, TValue>(); }
public ReadOnlyDictionary (IEqualityComparer<TKey> comparer, bool allowListEdit, bool allowItemEdit) { this.AllowListEdit = allowListEdit; this.AllowItemEdit = allowItemEdit; this.Dictionary = new Dictionary<TKey, TValue>(comparer); }
public ReadOnlyDictionary (IDictionary<TKey, TValue> dictionary, bool allowListEdit = false, bool allowItemEdit = false) : this(allowListEdit, allowItemEdit) { foreach (var pair in dictionary) { this.Dictionary.Add(pair.Key, pair.Value); } }
public ReadOnlyDictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer, bool allowListEdit = false, bool allowItemEdit = false) : this(comparer, allowListEdit, allowItemEdit) { foreach (var pair in dictionary) { this.Dictionary.Add(pair.Key, pair.Value); } }
#endregion Constructors.
#region Properties.
public int Count { get { return (this.Dictionary.Count); } }
public IEqualityComparer<TKey> Comparer { get { return (this.Dictionary.Comparer); } }
#endregion Properties.
#region Methods.
private void ThrowItemReadOnlyException () { if (this.AllowListEdit) { throw (new NotSupportedException("This collection does not allow editing items.")); } }
private void ThrowListReadOnlyException () { if (this.AllowItemEdit) { throw (new NotSupportedException("This collection does not allow adding and removing items.")); } }
public bool ContainsValue (TValue value) { return (this.Dictionary.ContainsValue(value)); }
public void Clear () { this.ThrowListReadOnlyException(); this.Dictionary.Clear(); }
#endregion Methods.
#region Interface Implementation: IEnumerable<KeyValuePair<TKey, TValue>>.
IEnumerator IEnumerable.GetEnumerator () { return (this.Dictionary.GetEnumerator()); }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () { return (this.Dictionary.GetEnumerator()); }
#endregion Interface Implementation: IEnumerable<KeyValuePair<TKey, TValue>>.
#region Interface Implementation: ICollection<KeyValuePair<TKey, TValue>>.
//public int Count { get { return (this.Dictionary.Count); } }
//public bool IsReadOnly { get { return (this.AllowListEdit); } }
//public bool Contains (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
//public void Clear () { throw (new NotImplementedException()); }
//public void Add (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
//public void CopyTo (KeyValuePair<TKey, TValue> [] array, int arrayIndex) { throw (new NotImplementedException()); }
//public bool Remove (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
#endregion Interface Implementation: ICollection<KeyValuePair<TKey, TValue>>.
#region Interface Implementation: IDictionary<TKey, TValue>.
//====================================================================================================
// Interface Implementation: IDictionary<TKey, TValue>.
//====================================================================================================
public Dictionary<TKey, TValue>.KeyCollection Keys { get { return (this.Dictionary.Keys); } }
public Dictionary<TKey, TValue>.ValueCollection Values { get { return (this.Dictionary.Values); } }
public TValue this [TKey key] { get { return (this.Dictionary [key]); } set { this.ThrowItemReadOnlyException(); this.Dictionary [key] = value; } }
public void Add (TKey key, TValue value) { this.ThrowListReadOnlyException(); this.Dictionary.Add(key, value); }
public bool ContainsKey (TKey key) { return (this.Dictionary.ContainsKey(key)); }
public bool Remove (TKey key) { this.ThrowListReadOnlyException(); return (this.Dictionary.Remove(key)); }
public bool TryGetValue (TKey key, out TValue value) { return (this.Dictionary.TryGetValue(key, out value)); }
#endregion Interface Implementation: IDictionary<TKey, TValue>.
}
Dictionary<TKey, TValue> implements the ICollection<KeyValuePair<TKey, TValue>> interface explicitly.
As you can see on the MSDN page for Dictionary, these methods are listed under "Explicit interface implementations".
Explicitly implementing an interface means that those methods will not be available through the concrete type. You'll have to cast the dictionary to an ICollection<T> to be able to call them.
Dictionary<int, int> dict = new Dictionary<int, int>();
bool sync = dict.IsSynchronized; // not allowed
ICollection<KeyValuePair<int, int>> dict = new Dictionary<int, int>();
bool sync = dict.IsSynchronized; // allowed
More on explicit interface implementations: http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx
Related
I have a Dictionary with a custom hashing function. I want to test the hash function, because even though it returns different hash results for my test values, some of them may still map to the same bucket due to the modulo % operation. So how to check if there are collisions in C# Dictionary with custom hash function and improve that function?
This is a development test to fine-tune the hash function and won't go into production so no worries about the changes in internal implementation in other versions!!!
In C++ it's possible to get the map's bucket size to check the collision status but I couldn't find a way to do that in C#. How can I know if Dictionary has been collided?
You can get internal buckets in the following way:
var dictionary = new Dictionary<string, int>();
dictionary.Add("a", 8);
dictionary.Add("b", 1);
var buckets = dictionary.GetType().GetField("_buckets", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dictionary); // use "buckets" for 4.x
You're probably better off creating a custom Dictionary implementation that changes the Add and Remove methods to check for hash collisions based on the computer GetHashCode of the elements. You can compose with a "real" Dictionary internally to do the real work of storing the elements.
Here's a sample version. You could optimize the Add and Remove methods depending on the type of hashes your expecting.
public class CollisionDetectingDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> InternalDictionary = new Dictionary<TKey, TValue>();
private readonly List<int> HashCodesInDictionary = new List<int>();
public event Action<int, TKey, IEnumerable<TKey>> HashCollision;
public TValue this[TKey key] { get => InternalDictionary[key]; set => InternalDictionary[key] = value; }
public ICollection<TKey> Keys => InternalDictionary.Keys;
public ICollection<TValue> Values => InternalDictionary.Values;
public int Count => InternalDictionary.Count;
public bool IsReadOnly => false;
public void Add(TKey key, TValue value)
{
Add(new KeyValuePair<TKey, TValue>(key, value));
}
public void Add(KeyValuePair<TKey, TValue> item)
{
var hashCode = item.Key.GetHashCode();
if (HashCodesInDictionary.Contains(hashCode))
{
var collisions = GetKeysByHashCode(hashCode);
HashCollision?.Invoke(hashCode, item.Key, collisions);
}
Add(item);
}
private IEnumerable<TKey> GetKeysByHashCode(int hashCode)
{
foreach (var key in Keys)
{
if(key.GetHashCode() == hashCode)
{
yield return key;
}
}
}
public void Clear()
{
InternalDictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return InternalDictionary.Contains(item);
}
public bool ContainsKey(TKey key)
{
return InternalDictionary.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((IDictionary<TKey,TValue>)InternalDictionary).CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return InternalDictionary.GetEnumerator();
}
public bool Remove(TKey key)
{
var hashCode = key.GetHashCode();
if(GetKeysByHashCode(hashCode).Count() == 1)
{
HashCodesInDictionary.Remove(hashCode);
}
return InternalDictionary.Remove(key);
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return InternalDictionary.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return InternalDictionary.GetEnumerator();
}
}
I have an OrderedDictionary with int keys and System.Drawing.Rectangle values. JSON.NET won't serialize the OrderedDictionary...it returns an empty object. I wrote a custom converter, but I wondered if there was an easier way. Thinking that JSON.NET might use the presence of a typed enumerator as the trigger to use its built-in code for serializing and deserializing a Dictionary<TKey, TValue> I tried this:
class Program
{
static void Main(string[] args)
{
var test = new OrderedDictionary<int, Rectangle>();
test.Add(1, new Rectangle(0, 0, 50, 50));
test.Add(42, new Rectangle(1, 1, 1, 1));
string s = JsonConvert.SerializeObject(test);
var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<int, Rectangle>>(s);
var someRect = deserialized[(object)1]; // someRect is null
var someOtherRect = (Rectangle)deserialized["1"]; // InvalidCastException
}
}
public class OrderedDictionary<TKey, TValue> : OrderedDictionary, IEnumerable<KeyValuePair<TKey, TValue>>
{
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
foreach (TKey key in Keys)
{
yield return new KeyValuePair<TKey, TValue>(key, (TValue)this[key]);
}
}
}
Serialization works perfectly. However, when I deserialize, the keys in the dictionary become strings and the Rectangles are JObjects that can't be cast to Rectangle. Is there something I can add to my OrderedDictionary<> class that will allow for proper deserialization with JSON.NET? Thanks.
Your problem is that, although you've added an enumerator, things like the indexer cannot be overridden. So what you're getting is the default implementation of the non-generic OrderedDictionary which doesn't give you a typed result.
So, instead of inheriting, you need a facade that fully implements the generic interface.
You'll need to verify my class (I just made the test work). I've also cheated with the Keys and Values properties (they're not often used) and some of the other ICollection methods. Just lazy :)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using Newtonsoft.Json;
using Xunit;
namespace XUnitTestProject1
{
public class UnitTest1
{
[Fact]
public void TestJsonRectange()
{
var test = new OrderedDictionary<int, Rectangle>();
test.Add(1, new Rectangle(0, 0, 50, 50));
test.Add(42, new Rectangle(1, 1, 1, 1));
string json = JsonConvert.SerializeObject(test);
var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<int, Rectangle>>(json);
object someRect = deserialized[1];
Assert.NotNull(someRect);
Assert.True(someRect is Rectangle);
}
[Fact]
public void TestJsonString()
{
var test = new OrderedDictionary<string, string>();
test.Add("1", "11");
test.Add("42", "4242");
string json = JsonConvert.SerializeObject(test);
var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<string, string>>(json);
object something = deserialized["1"];
Assert.NotNull(something);
Assert.True(something is string);
}
public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly OrderedDictionary dic = new OrderedDictionary();
public TValue this[TKey key] { get { return (TValue)dic[key]; } set { dic[key] = value; } }
public void Add(KeyValuePair<TKey, TValue> item)
{
dic.Add(item.Key, item.Value);
}
public void Add(TKey key, TValue value)
{
dic.Add(key, value);
}
public void Clear() { dic.Clear(); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { }
public int Count { get { return dic.Count; } }
public bool IsReadOnly { get { return false; } }
public bool Contains(TKey key) { return dic.Contains(key); }
public bool ContainsKey(TKey key) { return dic.Contains(key); }
public bool Remove(TKey key) { dic.Remove(key); return true; }
public bool TryGetValue(TKey key, out TValue value) { value = default(TValue); return false; }
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { return false; }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in dic)
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private static readonly TKey[] keys = new TKey[0];
private static readonly TValue[] values = new TValue[0];
ICollection<TKey> IDictionary<TKey, TValue>.Keys { get { return keys; } }
ICollection<TValue> IDictionary<TKey, TValue>.Values { get { return values; } }
}
}
}
I made a class that uses a SortedDictionary to store and manipulate data. The class works great except when it is implemented in a multi-threaded environment. Now, I would like to make the class thread safe by writing a wrapper class for the internal SortedDictionary class. I would like to use the Reader-Writer Locks to implement this but for now, I'm having problems just writing the wrapper class itself. Specifically, I'm not sure how to implement the Enumerator for the dictionary. Here is my complete code for the class as it stands now.
public class ConcurrentSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Variables
SortedDictionary<TKey, TValue> _dict;
#endregion
#region Constructors
public ConcurrentSortedDictionary()
{
_dict = new SortedDictionary<TKey, TValue>();
}
public ConcurrentSortedDictionary(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary, comparer);
}
#endregion
#region Properties
public IComparer<TKey> Comparer
{
get
{
return _dict.Comparer;
}
}
public int Count
{
get
{
return _dict.Count;
}
}
public TValue this[TKey key]
{
get
{
return _dict[key];
}
set
{
_dict[key] = value;
}
}
public SortedDictionary<TKey, TValue>.KeyCollection Keys
{
get
{
return new SortedDictionary<TKey,TValue>.KeyCollection(_dict);
}
}
public SortedDictionary<TKey, TValue>.ValueCollection Values
{
get
{
return new SortedDictionary<TKey, TValue>.ValueCollection(_dict);
}
}
#endregion
#region Methods
public void Add(TKey key, TValue value)
{
_dict.Add(key, value);
}
public void Clear()
{
_dict.Clear();
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return _dict.ContainsValue(value);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
_dict.CopyTo(array, index);
}
public override bool Equals(Object obj)
{
return _dict.Equals(obj);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
public override int GetHashCode()
{
return _dict.GetHashCode();
}
public bool Remove(TKey key)
{
return _dict.Remove(key);
}
public override string ToString()
{
return _dict.ToString();
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
}
#endregion
}
When I compile the code, I get the error message:
'ConcurrentSortedDictionary' does not implement interface
member 'System.Collections.IEnumerable.GetEnumerator()'.
'ConcurrentSortedDictionary.GetEnumerator()' cannot
implement 'System.Collections.IEnumerable.GetEnumerator()' because it
does not have the matching return type of
'System.Collections.IEnumerator'.
I looked at several posts here relating to this as a reference:
How do I implement IEnumerable in my Dictionary wrapper class that implements IEnumerable<Foo>?
What's the best way of implementing a thread-safe Dictionary?
But I don't see what I'm doing wrong. Any assistance greatly appreciated.
The problem is here:
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
You need:
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dict.GetEnumerator();
}
The second non-generic GetEnumerator() is an explicit interface implementation and is needed as an unfortunate throwback to the days before generics and generic collections existed in C#.
See also: IEnumerable<T> provides two GetEnumerator methods - what is the difference between them? (and in particular Michael B's answer).
However if you want enumeration to be thread-safe along with the rest of your class, you may also need to write your own thread-safe IEnumerator type which cooperates with the reader/writer locks in your class!
After implementing the suggestion by dvnrrs, I now have the class working well. I even added a wrapper class for the IEnumerable interface to protect enumerations of the SortedDictionary (code modified from this example here: http://www.codeproject.com/Articles/56575/Thread-safe-enumeration-in-C). Here is the updated code with the Reader-Writer Locks included:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
public class SafeEnumerator<T> : IEnumerator<T>
{
#region Variables
// this is the (thread-unsafe)
// enumerator of the underlying collection
private readonly IEnumerator<T> _enumerator;
// this is the object we shall lock on.
private ReaderWriterLockSlim _lock;
#endregion
#region Constructor
public SafeEnumerator(IEnumerator<T> inner, ReaderWriterLockSlim readWriteLock)
{
_enumerator = inner;
_lock = readWriteLock;
// Enter lock in constructor
_lock.EnterReadLock();
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
// .. and exiting lock on Dispose()
// This will be called when the foreach loop finishes
_lock.ExitReadLock();
}
#endregion
#region Implementation of IEnumerator
// we just delegate actual implementation
// to the inner enumerator, that actually iterates
// over some collection
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
_enumerator.Reset();
}
public T Current
{
get
{
return _enumerator.Current;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
#endregion
}
public class ConcurrentSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Variables
private ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim();
SortedDictionary<TKey, TValue> _dict;
#endregion
#region Constructors
public ConcurrentSortedDictionary()
{
_dict = new SortedDictionary<TKey, TValue>();
}
public ConcurrentSortedDictionary(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary, comparer);
}
#endregion
#region Properties
public IComparer<TKey> Comparer
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Comparer;
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public int Count
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Count;
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public TValue this[TKey key]
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict[key];
}
finally
{
_readWriteLock.ExitReadLock();
}
}
set
{
_readWriteLock.EnterWriteLock();
try
{
_dict[key] = value;
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
}
public SortedDictionary<TKey, TValue>.KeyCollection Keys
{
get
{
_readWriteLock.EnterReadLock();
try
{
return new SortedDictionary<TKey, TValue>.KeyCollection(_dict);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public SortedDictionary<TKey, TValue>.ValueCollection Values
{
get
{
_readWriteLock.EnterReadLock();
try
{
return new SortedDictionary<TKey, TValue>.ValueCollection(_dict);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
#endregion
#region Methods
public void Add(TKey key, TValue value)
{
_readWriteLock.EnterWriteLock();
try
{
_dict.Add(key, value);
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public void Clear()
{
_readWriteLock.EnterWriteLock();
try
{
_dict.Clear();
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public bool ContainsKey(TKey key)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ContainsKey(key);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool ContainsValue(TValue value)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ContainsValue(value);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
_readWriteLock.EnterReadLock();
try
{
_dict.CopyTo(array, index);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public override bool Equals(Object obj)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Equals(obj);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return new SafeEnumerator<KeyValuePair<TKey, TValue>>(_dict.GetEnumerator(), _readWriteLock);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new SafeEnumerator<KeyValuePair<TKey, TValue>>(_dict.GetEnumerator(), _readWriteLock);
}
public override int GetHashCode()
{
_readWriteLock.EnterReadLock();
try
{
return _dict.GetHashCode();
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool Remove(TKey key)
{
_readWriteLock.EnterWriteLock();
try
{
return _dict.Remove(key);
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public override string ToString()
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ToString();
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool TryGetValue(TKey key, out TValue value)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.TryGetValue(key, out value);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
#endregion
}
I'm completely new to C#, so I'm about to make a horrible attempt at my own version of an OrderedDictionary unless someone can suggest an alternative.
I need to be able to access my elements by array index, retaining the order they were added, and I also will be frequently updating individual elements using their key.
Is there a collection that allows this on the phone?
If I keep a List and Dictionary will they both be pointing to the same item or is there some kind of pointer thing I have to do?:
Item i = new Item();
list.Add(i);
dict.Add("key", i);
Here's my implementation (comes from the open source OpenNETCF Extensions library):
public class OrderedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private Dictionary<TKey, TValue> m_dictionary;
private List<TValue> m_list = new List<TValue>();
private object m_syncRoot = new object();
public OrderedDictionary()
{
m_dictionary = new Dictionary<TKey, TValue>();
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
{
m_dictionary = new Dictionary<TKey, TValue>(comparer);
}
public void Add(TKey key, TValue value)
{
lock (m_syncRoot)
{
m_dictionary.Add(key, value);
m_list.Add(value);
}
}
public TValue this[int index]
{
get { return m_list[index]; }
}
public TValue this[TKey key]
{
get { return m_dictionary[key]; }
}
public int Count
{
get { return m_dictionary.Count; }
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get { return m_dictionary.Keys; }
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get { return m_dictionary.Values; }
}
public void Clear()
{
lock (m_syncRoot)
{
m_dictionary.Clear();
m_list.Clear();
}
}
public bool ContainsKey(TKey key)
{
return m_dictionary.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return m_dictionary.ContainsValue(value);
}
public void Insert(int index, TKey key, TValue value)
{
lock (m_syncRoot)
{
m_list.Insert(index, value);
m_dictionary.Add(key, value);
}
}
public void Remove(TKey key)
{
lock (m_syncRoot)
{
if (ContainsKey(key))
{
var existing = m_dictionary[key];
m_list.Remove(existing);
m_dictionary.Remove(key);
}
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Using a List and a Dictionary is probably a good option actually. The "pointer thing" that you're talking about happens by default for Objects in .NET (any class and/or structure). All objects in .NET are passed around by reference.
So, if you use:
Item i = new Item();
list.Add(i);
dict.Add("key",i);
Console.WriteLine(list.Last() == dict["key"]);
Your output will be "true".
Best of luck!
I won't suggest using OrderedDictionary, since it's a non-generic container.
However, if you just want to use it like always. You can port Mono's version of OrderedDictionary.
https://github.com/mono/mono/blob/master/mcs/class/System/System.Collections.Specialized/OrderedDictionary.cs
Here's some tips if you want to port this:
Remove any unavailable interface
Remove serialization-related code
Replace ArrayList with List<object>
Replace Hashtable with Dictionary<object, object>
I thought of solution below because the collection is very very small. But what if it was big?
private Dictionary<string, OfTable> _folderData = new Dictionary<string, OfTable>();
public Dictionary<string, OfTable> FolderData
{
get { return new Dictionary<string,OfTable>(_folderData); }
}
With List you can make:
public class MyClass
{
private List<int> _items = new List<int>();
public IList<int> Items
{
get { return _items.AsReadOnly(); }
}
}
That would be nice!
Thanks in advance, Cheers & BR - Matti
NOW WHEN I THINK THE OBJECTS IN COLLECTION ARE IN HEAP. SO MY SOLUTION DOES NOT PREVENT THE CALLER TO MODIFY THEM!!! CAUSE BOTH Dictionary s CONTAIN REFERENCES TO SAME OBJECT. DOES THIS APPLY TO List EXAMPLE ABOVE?
class OfTable
{
private int _table;
private List<int> _classes;
private string _label;
public OfTable()
{
_classes = new List<int>();
}
public int Table
{
get { return _table; }
set { _table = value; }
}
public List<int> Classes
{
get { return _classes; }
set { _classes = value; }
}
public string Label
{
get { return _label; }
set { _label = value; }
}
}
so how to make this immutable??
It's not difficult to roll your own ReadOnlyDictionary<K,V> wrapper class. Something like this:
public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _dictionary;
public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}
public int Count
{
get { return _dictionary.Count; }
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}
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; }
}
public TValue this[TKey key] // Item
{
get { return _dictionary[key]; }
}
#region IDictionary<TKey, TValue> Explicit Interface Implementation
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
throw new NotSupportedException("Dictionary is read-only.");
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
throw new NotSupportedException("Dictionary is read-only.");
}
TValue IDictionary<TKey, TValue>.this[TKey key] // Item
{
get { return _dictionary[key]; }
set { throw new NotSupportedException("Dictionary is read-only."); }
}
#endregion
#region ICollection<T> Explicit Interface Implementation
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException("Collection is read-only.");
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
throw new NotSupportedException("Collection is read-only.");
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return _dictionary.Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dictionary.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get { return true; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException("Collection is read-only.");
}
#endregion
#region IEnumerable Explicit Interface Implementation
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_dictionary).GetEnumerator();
}
#endregion
}
If you're using C#3 or later then you could knock-up a matching AsReadOnly extension method too:
public static class ReadOnlyDictionaryHelper
{
public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
var temp = dictionary as ReadOnlyDictionary<TKey, TValue>;
return temp ?? new ReadOnlyDictionary<TKey, TValue>(dictionary);
}
}
And then return the read-only wrapper from your property:
// in C#2
return new ReadOnlyDictionary<string, OfTable>(_folderData);
// in C#3 or later
return _folderData.AsReadOnly();
Use ReadOnlyCollection<T> class.
An instance of the ReadOnlyCollection generic class is always read-only. A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes. See Collection for a modifiable version of this class.
--EDIT--
Checkout a trivial dictionary wrapper here. And A Generic Read-Only Dictionary by Richard Carr.