Efficient, Immutable, Extensible Collections for .NET [duplicate] - c#

This question already has answers here:
Immutable collections?
(10 answers)
Closed 9 years ago.
It seems to me there is an extreme lack of safe, immutable collection types for .NET, in particular BCL but I've not seen much work done outside either. Do anyone have any pointers to a (preferably) production quality, fast, immutable collections library for .NET. A fast list type is essential. I'm not yet prepared to switch to F#.
*Edit: Note to searchers, this is being rolled into the BCL soon: .NET immutable collections

You might want to take a look at the Microsoft.FSharp.Collections namespace in the FSharp.Core assembly. You do not have to program in F# to make use of these types.
Keep in mind that the names will be different when used from outside F#. For example, the Map in F# is known as FSharpMap from C#.

The .NET BCL team has released a Immutable Collections preview for .NET 4.5

Functional-dotnet by Alexey Romanov
Sasa by Sandro Magi
Kinet by Tony Morris

I wrote an ImmutableList<T> class some time ago :
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class ImmutableList<T> : IList<T>, IEquatable<ImmutableList<T>>
{
#region Private data
private readonly IList<T> _items;
private readonly int _hashCode;
#endregion
#region Constructor
public ImmutableList(IEnumerable<T> items)
{
_items = items.ToArray();
_hashCode = ComputeHash();
}
#endregion
#region Public members
public ImmutableList<T> Add(T item)
{
return this
.Append(item)
.AsImmutable();
}
public ImmutableList<T> Remove(T item)
{
return this
.SkipFirst(it => object.Equals(it, item))
.AsImmutable();
}
public ImmutableList<T> Insert(int index, T item)
{
return this
.InsertAt(index, item)
.AsImmutable();
}
public ImmutableList<T> RemoveAt(int index)
{
return this
.SkipAt(index)
.AsImmutable();
}
public ImmutableList<T> Replace(int index, T item)
{
return this
.ReplaceAt(index, item)
.AsImmutable();
}
#endregion
#region Interface implementations
public int IndexOf(T item)
{
if (_items == null)
return -1;
return _items.IndexOf(item);
}
public bool Contains(T item)
{
if (_items == null)
return false;
return _items.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
if (_items == null)
return;
_items.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
if (_items == null)
return 0;
return _items.Count;
}
}
public IEnumerator<T> GetEnumerator()
{
if (_items == null)
return Enumerable.Empty<T>().GetEnumerator();
return _items.GetEnumerator();
}
public bool Equals(ImmutableList<T> other)
{
if (other == null || this._hashCode != other._hashCode)
return false;
return this.SequenceEqual(other);
}
#endregion
#region Explicit interface implementations
void IList<T>.Insert(int index, T item)
{
throw new InvalidOperationException();
}
void IList<T>.RemoveAt(int index)
{
throw new InvalidOperationException();
}
T IList<T>.this[int index]
{
get
{
if (_items == null)
throw new IndexOutOfRangeException();
return _items[index];
}
set
{
throw new InvalidOperationException();
}
}
void ICollection<T>.Add(T item)
{
throw new InvalidOperationException();
}
void ICollection<T>.Clear()
{
throw new InvalidOperationException();
}
bool ICollection<T>.IsReadOnly
{
get { return true; }
}
bool ICollection<T>.Remove(T item)
{
throw new InvalidOperationException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region Overrides
public override bool Equals(object obj)
{
if (obj is ImmutableList<T>)
{
var other = (ImmutableList<T>)obj;
return this.Equals(other);
}
return false;
}
public override int GetHashCode()
{
return _hashCode;
}
#endregion
#region Private methods
private int ComputeHash()
{
if (_items == null)
return 0;
return _items
.Aggregate(
983,
(hash, item) =>
item != null
? 457 * hash ^ item.GetHashCode()
: hash);
}
#endregion
}
All methods that modify the collection return a modified copy. In order to fulfill with the IList<T> interface contract, the standard Add/Remove/Delete/Clear methods are implemented explicitly, but they throw an InvalidOperationException.
This class uses a few non-standard extension methods, here they are :
public static class ExtensionMethods
{
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item)
{
return source.Concat(new[] { item });
}
public static IEnumerable<T> SkipFirst<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
bool skipped = false;
foreach (var item in source)
{
if (!skipped && predicate(item))
{
skipped = true;
continue;
}
yield return item;
}
}
public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> source, int index)
{
return source.Where((it, i) => i != index);
}
public static IEnumerable<T> InsertAt<T>(this IEnumerable<T> source, int index, T item)
{
int i = 0;
foreach (var it in source)
{
if (i++ == index)
yield return item;
yield return it;
}
}
public static IEnumerable<T> ReplaceAt<T>(this IEnumerable<T> source, int index, T item)
{
return source.Select((it, i) => i == index ? item : it);
}
}
And here's a helper class to create instances of ImmutableList<T> :
public static class ImmutableList
{
public static ImmutableList<T> CreateFrom<T>(IEnumerable<T> source)
{
return new ImmutableList<T>(source);
}
public static ImmutableList<T> Create<T>(params T[] items)
{
return new ImmutableList<T>(items);
}
public static ImmutableList<T> AsImmutable<T>(this IEnumerable<T> source)
{
return new ImmutableList<T>(source);
}
}
Here's a usage example :
[Test]
public void Test_ImmutableList()
{
var expected = ImmutableList.Create("zoo", "bar", "foo");
var input = ImmutableList.Create("foo", "bar", "baz");
var inputSave = input.AsImmutable();
var actual = input
.Add("foo")
.RemoveAt(0)
.Replace(0, "zoo")
.Insert(1, "bar")
.Remove("baz");
Assert.AreEqual(inputSave, input, "Input collection was modified");
Assert.AreEqual(expected, actual);
}
I can't say it's production quality, as I haven't tested it thoroughly, but so far it seems to work just fine...

C5 springs to mind, but I'm not sure how fast it is. It has been around for years, and is very stable.
Additionally, List<T>.AsReadOnly() does the job rather well IMO, but unfortunately there is no equivalent for dictionaries or arbitrary ICollection<T>'s.

You may look at Extras or System.collections.concurrent tutorial

You could try BclExtras by JaredPar.

Related

Generic implementation of OrderDictionary in C# is showing ambiguous method warnings

Since C# has no generic implementation of the OrderedDictionary at the time of asking this question I downloaded one from here. To be very clear I am using this in the Unity game engine with MonoDevelop to code a game.
The implementation seems nicely put together however it gives me an ambiguous method call warning the solution to which I can't seem to figure out. Can somebody please explain me what is going on here and propose a possible solution to get rid of the warnings?
To be specific here are the similar method calls:
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();
}
And here is the error:
[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()'
Thanks in advance.
Edit:
Here is the source and its usage in the codebase that I have:
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);
}
}
}
This is how the above given OrderedDictionary is being used:
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);
}
}
}
}
To keep things confidential I've changed the code a bit here but overall it should deliver the idea. The most important statement in usage above is foreach (KeyValuePair<string, RoomInfo> room in roomSettingsModel.settings) which is the source of the warning. It's in this line that I think the compiler gets confused about which GetEnumerator() method to call.
Firstly, is that really the issue? Secondly, how do I resolve the problem?
I tried to follow what you've done, but it's a spaghetti of nested interfaces.
If you put breakpoints in every GetEnumerator() in OrderedDictionary, you may find it is not calling the enumerator you expect.
The problem, I think, is with trying to implement the non-generic IOrderedDictionary interface along with IDictionary<TKey, TValue>.
If you want generics, why do you need to maintain compatibilty with non-generic IOrderedDictionary?
If you follow (F12) the inheritance trail of IOrderedDictionary, it inherits IDictionary, ICollection, IEnumerable.
Then IDictionary<TKey, TValue> inherits from ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable.
I'm not quite sure what all your requirements are, but I would drop any interfaces you don't need to support. Don't provide code features that you don't need.
This is not completely of your making, but it is the consequence of trying to support multiple interfaces with lots of baggage of their own.
Based on your question, I would only support IDictionary<TKey, TValue> & IList<T>.
And their baggage ;)
For those curious about KeyedCollection, here's an implmentation that does most of what #Mubeen implemented in his code. This is not fully tested, so don't just do a copy->paste if you use this.
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;
}
}
}
I ended up writing a new implementation which is a pure generic wrapper around System.Collections.Specialized.OrderedDictionary.
Although this is not an answer to the original question, it is warning free and, like #ashley-pillay mentioned in his answer, implements only the necessary interfaces.
I am providing the implementation here in hopes of helping others since it's hard to find a good implementation of a warning free generic OrderedDictionary even after a lot of Googling.
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;
}
}
}

Does such a collection exist (functionality from Dictionary & HashSet)?

I am looking for a collection where no element can exist more than once, and are also indexed. Similar to Dictionary, but without Key, just Value. Similar to a HashSet, but indexed so I can easily retrieve an element without iterating over the collection. I hope this makes sense. :)
You can use a HashSet. It is "indexed", after all, performance would be lacking if it weren't.
Use the Contains method to "retrieve" an element. If you want to remove it as well, use Remove.
Both methods are O(1) operations.
You can use a Dictionary<T, T> for that and insert elements using Add(value, value).
However, that only makes sense if your type properly implements Equals(object) and GetHashCode(). If it doesn't, two different instanced will never be equal and the HashSet<T>'s Contains(T) method already tells you whether you have the element reference of nor.
HashSet class is best for your work. I won't allow duplicate entries.
Note that the HashSet.Add(T item) method returns a bool -- true if the item was added to the collection; false if the item was already present.
Simply you can add an Extension method to throw exception as
public static void AddOrThrow<T>(this HashSet<T> hash, T item)
{
if (!hash.Add(item))
throw new ValueExistingException();
}
The easiest way to do this is make a class that implements IList<T> but uses a List<T> and HashSet<T> internally. You then just have each method act on each collection as needed.
using System;
using System.Collections.Generic;
namespace Example
{
public class UniqueList<T> : IList<T>
{
private readonly List<T> _list;
private readonly HashSet<T> _hashset;
public UniqueList()
{
_list = new List<T>();
_hashset = new HashSet<T>();
}
public UniqueList(IEqualityComparer<T> comparer)
{
_list = new List<T>();
_hashset = new HashSet<T>(comparer);
}
void ICollection<T>.Add(T item)
{
Add(item);
}
public bool Add(T item)
{
var added = _hashset.Add(item);
if (added)
{
_list.Add(item);
}
return added;
}
public void RemoveAt(int index)
{
_hashset.Remove(_list[index]);
_list.RemoveAt(index);
}
public T this[int index]
{
get { return _list[index]; }
set
{
var oldItem = _list[index];
_hashset.Remove(oldItem);
var added = _hashset.Add(value);
if (added)
{
_list[index] = value;
}
else
{
//Put the old item back before we raise a exception.
_hashset.Add(oldItem);
throw new InvalidOperationException("Object already exists.");
}
}
}
public int IndexOf(T item)
{
return _list.IndexOf(item);
}
void IList<T>.Insert(int index, T item)
{
Insert(index, item);
}
public bool Insert(int index, T item)
{
var added = _hashset.Add(item);
if (added)
{
_list.Insert(index, item);
}
return added;
}
public void Clear()
{
_list.Clear();
_hashset.Clear();
}
public bool Contains(T item)
{
return _hashset.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
var removed = _hashset.Remove(item);
if (removed)
{
_list.Remove(item);
}
return removed;
}
public int Count
{
get { return _list.Count; }
}
public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
I did explicit implementations of Add and Insert so I could give them versions that returned a bool to tell if the operation succeeded or not. I could not return a value in the T this[int index] setter so I have it throw a InvalidOperationException if you attempt to insert a duplicate.
It does not throw if you do ICollection.Add on a duplicate, it just does not add it. This is because that is the behavior HashSet<T>.ICollection<T>.Add has and i wanted to mimic it.

How to make my InfiniteLoopingList class implement IEnumerable?

I am making a prototype application and for that I designed a class that behaves like an infinite looping list. That is, if my internal list contains 100 values, when I ask for the 101st value, I get the first, the 102nd yields the second, and so on, repeating.
So I would like to write the following code:
var slice = loopingListInstance.Skip(123).Take(5);
And for that I need to implement IEnumerable suitable, as I understand.
Here is my current code:
public class InfiniteLoopingList : IEnumerable<double>
{
double[] _values = File.ReadLines(#"c:\file.txt")
.Select(s => double.Parse(s, CultureInfo.InvariantCulture))
.ToArray();
int _size;
public InfiniteLoopingList()
{
_size = _values.Length;
}
public double this[int i]
{
get { return _values[i % _size]; }
set { _values[i % _size] = value; }
}
public IEnumerator<double> GetEnumerator()
{
return this.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
// ???? now what ?? :(
}
}
Since you implemented the indexer property, you could do it via the simplest way as follows:
public IEnumerator<double> GetEnumerator()
{
int i = 0;
while (true)
yield return this[i++];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
EDIT
Please notice, that this is not really infinite loop. This approach will only work until i = int.MaxValue. Thanks to #oleksii.
You don't need a class for this...
An extension method will do the trick:
public static class InfEx
{
public static IEnumerable<T> LoopForever<T>(this IEnumerable<T> src)
{
var data = new List<T>();
foreach(var item in src)
{
data.Add(item);
yield return item;
}
for(;;)
{
foreach(var item in data)
{
yield return item;
}
}
}
}
Now you can take a sequence and make it a looping, infinite sequence:
IEnumerable<Foo> mySeq = ...;
IEnumerable<Foo> infMySeq = mySeq.LoopForver();
IEnumerable<Foo> aSelectionOfInfMySeq = infMySeq.Skip(101).Take(5);
You can implement the IEnumerator interface:
class InifniteEnumerator<T> : IEnumerator<T> {
private int index = -1;
private IList<T> innerList;
private int repeatPos;
public InifniteEnumerator(IList<T> innerList, int repeatPos) {
this.innerList = innerList;
this.repeatPos = repeatPos;
}
public T Current {
get {
if (index == -1) {
throw new InvalidOperationException();
}
return this.innerList[index];
}
}
object IEnumerator.Current {
get {
return this.Current;
}
}
public void Dispose() {
}
public bool MoveNext() {
this.index++;
if (this.index == repeatPos) {
this.index = 0;
}
return true;
}
public void Reset() {
this.index = -1;
}
}
and then return an instance of it in the GetEnumerator methods:
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
public IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return new InifniteEnumerator(this, 100);
}

Serializing immutable collections with protobuf-net

I'm trying to serialize a class with protobuf-net which contains an immutable collection as a member.
The collection type, ImmutableList<T>, implements ICollection<T> but returns true for the IsReadOnly property. So any attempts to modify it throw an exception.
Protobuf-net seems to rely on being able to call Add(T) after creating a collection in order to populate it. This obviously won't work with an immutable collection, which is a shame because protobuf-net works beautifully with all my other data types, which are also immutable.
So my question is, what options do I have to be able to serialize such collections?
The code for ImmutableList<T> is listed below:
public sealed class ImmutableList<T> : IList<T>, IList
{
private readonly T[] m_Items;
public ImmutableList(IEnumerable<T> source)
{
m_Items = source.ToArray();
}
public T[] ToArray()
{
T[] newArray = new T[m_Items.Length];
m_Items.CopyTo(newArray, 0);
return newArray;
}
private static void ThrowNotSupported()
{
throw new NotSupportedException("The attempted operation was not supported as the collection is read-only.");
}
#region IList<T> Members
int IList.Add(object value)
{
ThrowNotSupported();
return -1;
}
void IList.Clear()
{
ThrowNotSupported();
}
void IList<T>.Insert(int index, T item)
{
ThrowNotSupported();
}
void IList.Insert(int index, object value)
{
ThrowNotSupported();
}
void IList.Remove(object value)
{
ThrowNotSupported();
}
void IList.RemoveAt(int index)
{
ThrowNotSupported();
}
void IList<T>.RemoveAt(int index)
{
ThrowNotSupported();
}
T IList<T>.this[int index]
{
get
{
return m_Items[index];
}
set
{
ThrowNotSupported();
}
}
object IList.this[int index]
{
get
{
return m_Items[index];
}
set
{
ThrowNotSupported();
}
}
public T this[int index]
{
get
{
return m_Items[index];
}
}
bool IList.Contains(object value)
{
return Array.IndexOf(m_Items, value) != -1;
}
int IList.IndexOf(object value)
{
return Array.IndexOf(m_Items, value);
}
public int IndexOf(T item)
{
return Array.IndexOf(m_Items, item);
}
bool IList.IsFixedSize
{
get
{
return true;
}
}
#endregion
#region ICollection<T> Members
void ICollection<T>.Add(T item)
{
ThrowNotSupported();
}
void ICollection<T>.Clear()
{
ThrowNotSupported();
}
bool ICollection<T>.Remove(T item)
{
ThrowNotSupported();
return false;
}
object ICollection.SyncRoot
{
get
{
return m_Items;
}
}
bool ICollection.IsSynchronized
{
get
{
return true;
}
}
public bool Contains(T item)
{
return IndexOf(item) != -1;
}
public void CopyTo(T[] array, int arrayIndex)
{
m_Items.CopyTo(array, arrayIndex);
}
void ICollection.CopyTo(Array array, int index)
{
m_Items.CopyTo(array, index);
}
public int Count
{
get
{
return m_Items.Length;
}
}
public bool IsReadOnly
{
get
{
return true;
}
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)m_Items).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_Items.GetEnumerator();
}
#endregion
}
At the moment there is no support for that; but it seems a reasonable scenario. It depends on what you mean (comments) by "out of the box"; if you mean "today, without code changed", it would need to use a surrogate against the DTO, or a shim property that exposed the immutable list as a mutable one; if you mean "moving forwards", I suspect we could look at list-constructors that take (perferable) IEnumerable[<T>] or (less preferable, but doable) T[], and simply construct the list after buffering the data.

The Most Efficient Implementation of UniqueQueue and UniqueReplacementQueue Collections in .NET

What is the most efficient (in terms of speed) implementation of UniqueQueue and UniqueReplacementQueue collections in .NET considering the fact that the speed of Enqueue and Dequeue operations is equally important.
UniqueQueue is a queue where duplicates are not possible. So if I push an element to the queue it is added in only case it doesn't already exist in the queue.
UniqueReplacementQueue is a queue where duplicates are not possible either. The difference is that if I push an element which already exists in the queue, it replaces the existing element at the same position. It makes sense for reference types.
My current implementation of UniqueQueue and UniqueReplacementQueue:
sealed class UniqueQueue<T> : IQueue<T>
{
readonly LinkedList<T> list;
readonly IDictionary<T, int> dictionary;
public UniqueQueue(LinkedList<T> list, IDictionary<T, int> dictionary)
{
this.list = list;
this.dictionary = dictionary;
}
public int Length
{
get { return list.Count; }
}
public T Dequeue()
{
if (list.Count == 0)
{
throw new InvalidOperationException("The queue is empty");
}
var element = list.First.Value;
dictionary.Remove(element);
list.RemoveFirst();
return element;
}
public void Enqueue(T element)
{
dictionary[element] = 0;
if (dictionary.Count > list.Count)
{
list.AddLast(element);
}
}
}
sealed class UniqueReplacementQueue<T> : IQueue<T>
{
readonly LinkedList<T> list;
readonly IDictionary<T, T> dictionary;
public UniqueReplacementQueue(LinkedList<T> list, IDictionary<T, T> dictionary)
{
this.list = list;
this.dictionary = dictionary;
}
public int Length
{
get { return list.Count; }
}
public T Dequeue()
{
if (list.Count == 0)
{
throw new InvalidOperationException("The queue is empty");
}
var element = dictionary[list.First.Value];
dictionary.Remove(element);
list.RemoveFirst();
return element;
}
public void Enqueue(T element)
{
dictionary[element] = element;
if (dictionary.Count > list.Count)
{
list.AddLast(element);
}
}
}
This is pretty old, but how about a class that has an internal HashSet, and Queue. A custom method for Enqueue firsts tries to add it to the hashset. if the HashSet.Add call returns false, we do not enqueue it. HashSet.Add() is an O(1) operation if the set is of a size large enough to hold all elements.
The only drawback to this is memory usage if this is a concern for you. Here is an implementation:
public class UniqueQueue<T> : IEnumerable<T> {
private HashSet<T> hashSet;
private Queue<T> queue;
public UniqueQueue() {
hashSet = new HashSet<T>();
queue = new Queue<T>();
}
public int Count {
get {
return hashSet.Count;
}
}
public void Clear() {
hashSet.Clear();
queue.Clear();
}
public bool Contains(T item) {
return hashSet.Contains(item);
}
public void Enqueue(T item) {
if (hashSet.Add(item)) {
queue.Enqueue(item);
}
}
public T Dequeue() {
T item = queue.Dequeue();
hashSet.Remove(item);
return item;
}
public T Peek() {
return queue.Peek();
}
public IEnumerator<T> GetEnumerator() {
return queue.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return queue.GetEnumerator();
}
}
The HashSet is used whenever it can because it is typically faster. This could be nicer if the maintainers of .NET marked these methods as virtual, but alas here we are.
How about this?
//the UniqueQueueItem has the key in itself,
//and implements the IUniqueQueueItemable to copy the other values.
//For example:
class TestUniqueQueueItem : IUniqueQueueItemable<TestUniqueQueueItem>
{
//Key
public int Id { get; set; }
public string Name { get; set; }
public override int GetHashCode()
{
return Id;
}
//To copy the other values.
public void CopyWith(TestUniqueQueueItem item)
{
this.Name = item.Name;
}
public override bool Equals(object obj)
{
return this.Id == ((TestUniqueQueueItem)obj).Id;
}
}
internal interface IUniqueQueueItemable<in T>
{
void CopyWith(T item);
}
class UniqueQueue<T> where T: IUniqueQueueItemable<T>
{
private readonly bool _isReplacementQueue;
private readonly Queue<T> _queue;
private readonly Dictionary<T, T> _dictionary;
public UniqueQueue(): this(false)
{
}
public UniqueQueue(bool isReplacementQueue)
{
_isReplacementQueue = isReplacementQueue;
_queue = new Queue<T>();
_dictionary = new Dictionary<T, T>();
}
public void Enqueue(T item)
{
if(!_dictionary.Keys.Contains(item))
{
_dictionary.Add(item, item);
_queue.Enqueue(item);
}
else
{
if(_isReplacementQueue)
{
//it will return the existedItem, which is the same key with the item
//but has different values with it.
var existedItem = _dictionary[item];
//copy the item to the existedItem.
existedItem.CopyWith(item);
}
}
}
public T Dequeue()
{
var item = _queue.Dequeue();
_dictionary.Remove(item);
return item;
}
}

Categories