I've got a tree-structure-class called ConfigNode (similar to SerializationInfo), which can store configuration values in a list and controls child nodes. When I add IEnumerable<ConfigNode> as interface to derive from, protobuf-net fails during serialization and causes a StackOverFlowException, even with the IgnoreListHandling flag set to true.
[Serializable, DataContract, ProtoContract(IgnoreListHandling = true)]
public class ConfigNode : Entity, ICloneable, INotifyPropertyChanged
{
[DataMember, ProtoMember(1)]
private Dictionary<String, PrimitiveSurrogate> values = new Dictionary<String, PrimitiveSurrogate>();
private Dictionary<String, ConfigNode> _Childs = new Dictionary<String, ConfigNode>();
[DataMember, ProtoMember(2)]
public Dictionary<String, ConfigNode> Childs
{
get
{
return _Childs;
}
private set
{
_Childs = value;
}
}
[DataMember, ProtoMember(3)]
public ConfigNode Parent { get; set; }
}
is working fine. PrimitiveSurrogate is a struct storing nullables of all commonly used "almost-primitives" like String, Guid, DataTime, float / double, bool, char, etc. The config values themselves will be added in a "Store"-method which is of no importance. It takes an object as parameter and tries to cast the type to one of the storable data types and stores it the strongly typed PrimitiveSurrogate. The entity base-class just provides a Name-property, nothing else.
But as soon as I add IEnumerable<ConfigNode> to the list of interfaces and add the appropriate methods (see below), any serialization attempts throw a StackOverflowException.
public void Add(ConfigNode child)
{
if (child == null)
throw new ArgumentNullException("child");
if (child.Name == null)
throw new ArgumentException("The child's name was null. The child cannot be added.");
child.Parent = this;
lock (this.Childs)
{
this.Childs[child.Name] = child;
}
}
public IEnumerator<ConfigNode> GetEnumerator()
{
lock (this.Childs)
{
return this.Childs.Values.Clone().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (this.Childs)
{
return this.Childs.Values.Clone().GetEnumerator();
}
}
"Clone" is an extension method and looks like this:
public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection)
{
return collection.ToList();
}
It seems like protobuf-net ignores the IgnoreListHandling-flag, can anyone help?
Nevermind, I found the mistake. After some changes I introduced a lock in the Childs-Property and in Childs.get() I was locking the property itself and not the backing field. This caused a StackOverflow due to the Monitor-Class trying to access the property and causing the get-Accessor to be accessed again.
It now (de)serializes smoothly.
Fixed version:
[DataMember, ProtoMember(2)]
public Dictionary<String, ConfigNode> Childs
{
get
{
lock (_Childs)
{
return _Childs;
}
}
private set
{
lock (_Childs)
{
_Childs = value;
}
}
}
Related
Recently, when handling collections of objects of the same (base-)class,
I´ve recently found myself writing something like this:
class SomeClass {
public bool PropertyA {get; set;}
}
class EncapsulatingClass {
private List<SomeClass> list = new();
private bool propA;
public bool PropertyA {
get { return propA; }
set {
propA = value;
foreach(SomeClass instance in list)
instance.PropertyA = value;
}
}
}
This is of course so I don´t have to use foreach every time I want to set a property for the collection. While this works fine, I feel like this requires a lot of code for something simple and a lot of repitition with each property.
Is there a better solution, like extracting the logic of "apply this for the property of the same name for each object in the list" into a function and just calling that in the setters?
There is the issue of ownership of the property. If you need to enforce synchronization such that setting PropertyA ins the encapsulating class, all the instances in the list also use the same value.
For example
class SomeClass
{
public SomeClass(EncapsulatingClass parent)
{
Parent=parent;
}
public EncapsulatingClass Parent { get; }
public bool PropertyA { get => Parent.PropertyA; }
}
class EncapsulatingClass
{
private List<SomeClass> list = new List<SomeClass>();
private bool propA;
public bool PropertyA
{
get { return propA; }
set
{
propA = value;
}
}
}
Otherwise, you have multiple PropertyA values, one for each instance, and then you have to decide which one is the master value, and what to do if some are different.
I'm wondering what it is you are doing to need this so often. It makes me think there's a flaw in the design of your application you could avoid by restructuring something but it's difficult to say without more information.
For your specific problem I would discard EncapsulatingClass and use the ForEach method on List<T> for a little more concise code:
myList.ForEach(s => s.PropertyA = true);
Alternatively, if you don't always use List<T> you can write your own extension method to work on all IEnumerables:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var t in source)
action(t);
}
// Call it just like previously:
myIEnumerable.ForEach(s => s.PropertyA = true);
Of course, this is still cumbersome if you need to do it a lot. But I suspect if you do, it's probably a flaw in the design.
I might approach this with a custom List class providing a single mass update method.
public class EasyUpdateList<T> : List<T>
{
public void UpdateAll(Action<T> update)
{
if (update == null)
return;
foreach (T item in this)
update(item);
}
}
Now you don't need a specific encapsulating class, you can just create a new EasyUpdateList and update any number of properties across the collection using the UpdateAll method.
EasyUpdateList<MyClass> list = new EasyUpdateList<MyClass>();
list.Add(instance1);
list.Add(instance2);
...
list.UpdateAll(x =>
{
x.Property1 = "Value1";
x.Property2 = "Value2";
});
This still uses a foreach loop but is much more generic and you don't have to change your other classes or write repeated code for each one.
Of course you could also achieve this with an extension method for a List class if you don't want a new class.
public static void UpdateAll<T>(this IList<T> list, Action<T> update)
{
if (update == null)
return;
foreach (T item in list)
update(item);
}
I am probably trying to do something that isn't possible. I am working with Castle.ActiveRecord / Nhibernate. I have been happy so far, but one thing I have always wanted to able to so is have something like:
[Property(ColumnType = "StringClob")]
public IDictionary<string, string> MetaData { get; set; }
This, for obvious reasons, this isn't possible at the moment. I started playing around with things to make this work and have come down to the following.
I have created a custom class that extends Dictionary
public class SerializableDictionary<TK, TV> : Dictionary<TK, TV>
{
public static explicit operator SerializableDictionary<TK, TV>(System.String serialized)
{
return JsonConvert.DeserializeObject<SerializableDictionary<TK, TV>>(serialized);
}
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And in my ActiveRecord model I have this:
[Property(ColumnType = "StringClob")]
public SerializableDictionary<string, string> MetaData { get; set; }
This works extremely well when I save the data. It gets serialized nicely and stores as a string. When I try to load a model though, I get the following error:
Unable to cast object of type 'System.String' to type 'MyNamespace.SerializableDictionary`2[System.String,System.String]'.
I have looked as deep as possible (using dotPeek) into where this is happening. The stack trace led me to:
NHibernate.Bytecode.Lightweight.AccessOptimizer.SetPropertyValues(Object target, Object[] values)
I am at a complete loss. I have scoured google to see if what I am trying to do is even possible. Any help is much appreciated.
I was able to figure this out after being pointed in the right direction by Mike Christensen
in the comments.
All of this code is probably overkill, and somethings are probably inefficient because I was doing it as fast as I could. But here it is...
I started with the same extend class as in the question (I modified it a little, because I'm still not sure how to implement the generics).
public class SerializableDictionary : Dictionary<string, object>
{
public static explicit operator SerializableDictionary(String serialized)
{
return JsonConvert.DeserializeObject<SerializableDictionary>(serialized);
}
public static explicit operator String(SerializableDictionary dict)
{
return JsonConvert.SerializeObject(dict);
}
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
I next had to create a custom SqlType for NHibernate. This class had to implement IUserType, which came with a bunch methods that had to be implemented (I'm not sure I implemented these the best way possible). This class now allows NHibernate to use the NullSafeSet to set the model property with the string from the database. There I was able to do the deserialization.
public class SerializableDictionaryType : IUserType
{
public new bool Equals(object x, object y)
{
return x != null && x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
string dbString = (string) NHibernateUtil.String.NullSafeGet(rs, names);
SerializableDictionary dict = JsonConvert.DeserializeObject<SerializableDictionary>(dbString);
return dict;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
NHibernateUtil.String.NullSafeSet(cmd, null, index);
return;
}
value = value.ToString();
NHibernateUtil.String.NullSafeSet(cmd, value, index);
}
public object DeepCopy(object value)
{
if (value == null) return null;
SerializableDictionary newDict = new SerializableDictionary();
foreach (KeyValuePair<string, object> item in (SerializableDictionary)value)
{
newDict.Add(item.Key, item.Value);
}
return newDict;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return JsonConvert.DeserializeObject<SerializableDictionary>(cached.ToString());
}
public object Disassemble(object value)
{
return JsonConvert.SerializeObject(value);
}
public SqlType[] SqlTypes
{
get
{
SqlType[] types = new SqlType[1];
types[0] = new SqlType(DbType.String);
return types;
}
}
public Type ReturnedType
{
get { return typeof (SerializableDictionary); }
}
public bool IsMutable
{
get { return false; }
}
}
Then on the model itself I had to put the following:
[Property(ColumnType = "MyNamespace.SerializableDictionaryType, MyNamespace")]
public SerializableDictionary UserData { get; set; }
This ColumnType refers to the custom type mapper that I created. If you notice I had to use the full namespace and class along with the assembly it's located in.
With all of this setup it works like a charm. I'm planning on optimizing things to make it look nicer and would still like to find a way to deal with generics instead of being stuck with:
<string, object>
I'll update the answer with anything I find.
Goal
I have a generic class GenericClass<T> and I want to pool instances.
I'm interested in seeing if I can get the syntax:
MyGenericPool = new GenericPool<GenericClass>();
// Or maybe it's MyGenericPool = new GenericPool<GenericClass<>>();
GenericClass<TGenericParam> GenericClassInstance =
MyGenericPool.Get<TGenericParam>();
(My understanding of generics says, no I can't, don't be silly the syntax doesn't exist / wouldn't work, but I'm intested in what others think).
Showing my workings
I'm a bit doubtful as from my understanding the types GenericClass<string> and GenericClass<int> aren't really related from the type system's point of view.
Now, I realise that I can get close, i.e.:
GenericClass<TGenericParam> GenericClassInstance =
GenericPool.Get<GenericClass<TGenericParam>>();
and then have the GenericPool just store a Dictionary<Type, ObjectPool<object>> somewhere.
I'm interested in seeing if I can avoid having to do that. I don't want to have to specify the generic type every time when, as the caller, i'm only changing the generic type parameter. I'd also like to be able to enforce (compile time) that all objects going into my GenericObjectPool<T> are of a set generic type (T<>).
I think the problem stems from not being able to treat a generic type parameter as being generic its self. If I could do that (can I already??) then maybe something like the below might work:
public class GenericClassPool<TGeneric> where TGeneric : class<>
{
private readonly Dictionary<Type, object> objectPools = new Dictionary<Type, object>();
private void EnsureObjectPoolExists<TGenericParam>()
{
if (!objectPools.ContainsKey(typeof(TGenericParam)))
{
objectPools.Add(typeof(TGenericParam), new ObjectPool<TGeneric<TGenericParam>>(() => Activator.CreateInstance(typeof(TGeneric<TGenericParam>)) as TGeneric<TGenericParam>));
}
}
private ObjectPool<TGeneric<TGenericParam>> GetPool<TGenericParam>()
{
EnsureObjectPoolExists<TGenericParam>();
return (objectPools[typeof(TGenericParam)] as ObjectPool<TGeneric<TGenericParam>>);
}
public void Add<TTypeParam>(TGeneric<TGenericParam> obj)
{
EnsureObjectPoolExists<TTypeParam>();
GetPool<TGenericParam>().Add(obj);
}
public TGeneric<TGenericParam> Get<TGenericParam>()
{
return GetPool<TGenericParam>().Get() as TGeneric<TGenericParam>;
}
}
Question
Can I get the syntax I want (at the top)? If not, how close can I get?
The solution / syntax you are trying to achieve doesn't work that way, because you can't use a generic type without its type parameters as the type parameter to another generic type.
However, you could achieve similar results with the following approach:
Create a base class for the class pool that requires you to supply the complete generic type
Create a derived class for the specific generic type
Something like that:
public class ObjectPool
{
Dictionary<Type, object> _objectPool = new Dictionary<Type, object>();
public void Add<TKey, TValue>(TValue value)
{
_objectPool.Add(typeof(TKey), value);
}
public TValue Get<TKey, TValue>() where TValue : class
{
object value;
if(_objectPool.TryGetValue(typeof(TKey), out value))
return value as TValue;
return null;
}
}
public class GenericClassPool : ObjectPool
{
public void Add<TGenericParam>(GenericClass<TGenericParam> obj)
{
Add<TGenericParam, GenericClass<TGenericParam>>(obj);
}
public GenericClass<TGenericParam> Get<TGenericParam>()
{
return Get<TGenericParam, GenericClass<TGenericParam>>();
}
}
Usage would then be like this:
var pool = new GenericClassPool();
pool.Add(new GenericClass<string> { Property = "String" });
pool.Add(new GenericClass<int> { Property = 0 });
GenericClass<string> firstObject = pool.Get<string>();
GenericClass<int> secondObject = pool.Get<int>();
The draw back of this solution is that you would need to create one pool class for each generic type you want to pool, so you potentially will have a lot of <className>Pool classes deriving from ObjectPool.
To make this usable, all real code needs to be in the ObjectPool class and only code that supplies the generic parameters remains in the derived classes.
I'd like to share my own pool classes. They have a similar API to the other code posted but are a bit more developed and flexible, in my totally biased opinion.
Single type object pool:
/// <summary>
/// Allows code to operate on a Pool<T> without casting to an explicit generic type.
/// </summary>
public interface IPool
{
Type ItemType { get; }
void Return(object item);
}
/// <summary>
/// A pool of items of the same type.
///
/// Items are taken and then later returned to the pool (generally for reference types) to avoid allocations and
/// the resulting garbage generation.
///
/// Any pool must have a way to 'reset' returned items to a canonical state.
/// This class delegates that work to the allocator (literally, with a delegate) who probably knows more about the type being pooled.
/// </summary>
public class Pool<T> : IPool
{
public delegate T Create();
public readonly Create HandleCreate;
public delegate void Reset(ref T item);
public readonly Reset HandleReset;
private readonly List<T> _in;
#if !SHIPPING
private readonly List<T> _out;
#endif
public Type ItemType
{
get
{
return typeof (T);
}
}
public Pool(int initialCapacity, Create createMethod, Reset resetMethod)
{
HandleCreate = createMethod;
HandleReset = resetMethod;
_in = new List<T>(initialCapacity);
for (var i = 0; i < initialCapacity; i++)
{
_in.Add(HandleCreate());
}
#if !SHIPPING
_out = new List<T>();
#endif
}
public T Get()
{
if (_in.Count == 0)
{
_in.Add(HandleCreate());
}
var item = _in.PopLast();
#if !SHIPPING
_out.Add(item);
#endif
return item;
}
public void Return( T item )
{
HandleReset(ref item);
#if !SHIPPING
Debug.Assert(!_in.Contains(item), "Returning an Item we already have.");
Debug.Assert(_out.Contains(item), "Returning an Item we never gave out.");
_out.Remove(item);
#endif
_in.Add(item);
}
public void Return( object item )
{
Return((T) item);
}
#if !SHIPPING
public void Validate()
{
Debug.Assert(_out.Count == 0, "An Item was not returned.");
}
#endif
}
Next, a multi-type pool.
There is no difference between using this class or using multiple Pool<T> yourself. But in some situations using this class will make code look cleaner, ie. eliminating if/else (type == foo) blocks.
/// <summary>
/// Represents a collection of pools for one or more object types.
/// </summary>
public class Pooler
{
private readonly List<IPool> _pools;
public Pooler()
{
_pools = new List<IPool>();
}
public void DefineType<T>(int initialCapacity, Pool<T>.Create createHandler, Pool<T>.Reset resetHandler)
{
var p = new Pool<T>(initialCapacity, createHandler, resetHandler);
_pools.Add(p);
}
public T Get<T>()
{
var p = GetPool(typeof (T));
if (p == null)
throw new Exception(string.Format("Pooler.Get<{0}>() failed; there is no pool for that type.", typeof(T)));
return ((Pool<T>)p).Get();
}
public void Return(object item)
{
var p = GetPool(item.GetType());
if (p == null)
throw new Exception(string.Format("Pooler.Get<{0}>() failed; there is no pool for that type.", item.GetType()));
p.Return(item);
}
private IPool GetPool(Type itemType)
{
foreach (var p in _pools)
{
if (p.ItemType == itemType)
{
return p;
}
}
return null;
}
}
As far as 'not having to specify the type parameter every time you access the pool', I often declare a concrete pool for a specific type that is frequently used.
public class GameObjectPool : Pool<GameObject>
{
public GameObjectPool(int initialCapacity)
:base(initialCapacity, CreateObject, ResetObject)
{
}
private GameObject CreateObject()
{ ... }
private GameObject ResetObject()
{ ... }
}
Then your code which was...
_pool = new Pool<GameObject>(10);
var obj = _pool.Get<GameObject>();
Can become...
_pool = new GameObjectPool(10);
var obj = _pool.Get();
Another option is...
using GameObjectPool=MyRootnamespace.Pool<GameObject>
Which can work if you have a ton of references to the pool, but they are all in the same code file.
I have class called GroupItem, i can store any type here say int, string, decimal, datetime etc.., Then, i have GroupItems which will store any groupItem. I'm using an arraylist to store all the groupItem.
public class GroupItem<T>
{
private string heading;
private List<T> items = new List<T>();
public GroupItem() { }
public string Heading
{
get { return heading; }
set { heading = value; }
}
public List<T> Items
{
get { return items; }
set { items = value; }
}
public void Add(T value)
{
this.items.Add(value);
}
public T this[int index]
{
get
{
return this.items[index];
}
}
}
public class GroupItems
{
private string groupName;
private List<object> items = new List<object>();
public string GroupName
{
get { return groupName; }
set { groupName = value; }
}
public GroupItems() { }
public void Add(object value)
{
this.items.Add(value);
}
public object this[int index]
{
get
{
return this.items[index];
}
}
}
I want to retrieve from GroupItems. How i can get generic item's values in groupItems?
I'm now inserting two items, datetime and int to groupitems. Now i want to retrieve groupitems[2] value but how i can convert this to groupItem without knowing what it is. Even we may get its genericarguments by getType().getGenericarguments()[0]. But how i can create an instance based upon that.
If the list is storing heterogeneous items, then I would suggest you need a common non-generic interface or base-class. So, say we have
interface IGroupItem {
// the non-generic members, and maybe
// "object Value {get;}" etc, and maybe
// "Type ItemTypr {get;}"
}
You would then have:
class GroupItem<T> : IGroupItem {...}
an you would then use
List<IGroupItem> ...
instead of ArrayList, or, franky, in place of GroupItems {...}
What I'd do is create a generic collection such as:
public class GroupItems<T> : List<GroupItem<T>>
{
}
If you need to extend the basic functionality of a list, you could also extend Collection<T> and override the methods you need:
public class GroupItems<T> : Collection<GroupItem<T>>
{
protected override void InsertItem(int index, T item)
{
// your custom code here
// ...
// and the actual insertion
base.InsertItem(index, item);
}
}
How about just replacing your GroupItems class with List<GroupItem<T>> ?
Depending on what you do with GroupItem you should either inherit from List/Collection as was offered by other or use a generic collection inside your class
e.g.
class GroupItem<T>
{
private List<T> items = new List<T>();
public void Add(T value)
{
items.Add(value);
}
public T Get()
{
//replace with some logic to detemine what to get
return items.First();
}
}
There are two situations that could be covered by your question:
You want to simply store a collection of GroupItem's of type T in the class GroupItems.
You want to store a collection of generic GroupItem's of any type in the class GroupItems. To better clarify, I mean that you could store GroupItem<DateTime> or GroupItem<int> in the same GroupItems class.
Here are some ways of going about storing and retrieving for both scenarios:
Same Type
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems<T>
{
private List<GroupItem<T>> mItems = new List<GroupItem<T>>();
public void Add(T item)
{
mItems.Add(item);
}
public T GetItem(int index)
{
return mItems[index];
}
}
Here you will build a collections that contain GroupItem's of the same time, so a collection of GroupItem<DateTime> for example. All the items will be of the same type.
Generic Type
public interface IGroupItem
{
// ... Common GroupItem properties and methods
}
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems
{
private List<IGroupItem> mItems = new List<IGroupItem>();
public void Add(IGroupItem item)
{
mItems.Add(item);
}
// This is a generic method to retrieve just any group item.
public IGroupItem GetItem(int index)
{
return mItems[index];
}
// This is a method that will get a group item at the specified index
// and then cast it to the specific group item type container.
public GroupItem<T> GetItem<T>(int index)
{
return (GroupItem<T>)mItems[index];
}
}
Here you will be able to build and maintain a single collection that can contain any GroupItem with any Type. So you could have a GroupItems collection that contains items of GroupItem<DateTime>, GroupItem<int>, etc.
Please note that none of these code examples take into account any erroneous circumstances.
Consider: you have a collection of items; the items may have any runtime type (string, int, etc.). Because of this, the static type of the collections items must be object.
It seems that you want to be able to retrieve items from the list with strong static typing. That's not possible without a lot of conditional logic (or reflection). For example:
object item = collection[0];
if (item is int)
//do something with an int
else if (item is string)
//do something with a string
Now suppose instead of "doing something" with the value of collection[0], we assign the value to a variable. We can do one of two things:
use the same variable for both cases, in which case the static type must be object.
use separate variables, in which case the static type will be string or int, but outside of the conditional logic, we can't know which variable holds the value of collection[0].
Neither option really solves the problem.
By creating GroupItem<T>, you add a level of indirection to this problem, but the underlying problem is still there. As an exercise, try reworking the example, but starting from "Consider: you have a collection of items; the items are of type GroupItem<T> where T may be any runtime type (string, int, etc.)."
Thanks for your inputs.
I have resolved it myself using multiple overloading methods to resolve this.
for example:
private void Print(GroupItem<string> items)
{
///custom coding
}
private void Print(GroupItem<int> items)
{
///custom coding
}
Though its not efficient enough, i want to do in this way as it was .net 2.0.
I'm now improving this in .Net 4.0 with new algorithm.
Thanks a lot for all of your helps.
I have a Dictionary<int, object> where the int is a property of obj. Is there a better data structure for this? I feel like using a property as the key is redundant.
This Dictionary<int, obj> is a field in a container class that allows for random indexing into the obj values based on an int id number. The simplified (no exception handling) indexer in the container class would look like:
obj this[int id]
{
get{ return this.myDictionary[id];}
}
where myDictionary is the aforementioned Dictionary<int, obj> holding the objects.
This may be the typical way of quick random access but I wanted to get second opinions.
There's no concrete class in the framework that does this. There's an abstract one though, KeyedCollection. You'll have to derive your own class from that one and implement the GetKeyForItem() method. That's pretty easy, just return the value of the property by which you want to index.
That's all you need to do, but do keep an eye on ChangeItemKey(). You have to do something meaningful when the property that you use as the key changes value. Easy enough if you ensure that the property is immutable (only has a getter). But quite awkward when you don't, the object itself now needs to have awareness of it being stored in your collection. If you don't do anything about it (calling ChangeItemKey), the object gets lost in the collection, you can't find it back. Pretty close to a leak.
Note how Dictionary<> side-steps this problem by specifying the key value and the object separately. You may still not be able to find the object back but at least it doesn't get lost by design.
There is a KeyedCollection class.
EDIT: The KeyedCollection can use a dictionary internally, but it cleaner interface for this particular scenario than a raw dictionary since you can lookup by values directly. Admittedly I don't find it very useful in general.
You can implement your own KeyedCollection trivially if the extra overhead that comes with the factory settings isn't worth it. The original KeyedCollection in System.Collections.ObjectModel is internally a Dictionary<TKey, TItem> and a List<TItem> which means you can have operations defined on both IList<> and IDictionary<>. For e.g., you can insert, access by index, traverse collection in the inserted order (all which IList<> facilitates) and at the same time you can have quick lookups based on key (with the help of dictionary). This means that when you're adding or removing an item they have to be performed on both underlying collections, apart from the small memory overhead to hold the extra List<> (but the objects are not duplicated as such). Though the addition speeds are not affected much (List<> addition is O(1)), removal speed is affected a little.
If you don't care about insertion order and accessing by index:
public class KeyedCollection<TKey, TItem> : ICollection<TItem>
{
MemberInfo _keyInfo;
Func<TItem, TKey> _keySelector;
Dictionary<TKey, TItem> _dict;
public TItem this[TKey key]
{
get { return _dict[key]; }
}
public int Count
{
get { return _dict.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public ICollection<TKey> Keys
{
get { return _dict.Keys; }
}
private ICollection<TItem> Items
{
get { return _dict.Values; }
}
public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
{
var keyExpression = keySelector.Body as MemberExpression;
if (keyExpression != null)
_keyInfo = keyExpression.Member;
_keySelector = keySelector.Compile();
_dict = new Dictionary<TKey, TItem>(comparer);
}
private TKey GetKeyForItem(TItem item)
{
return _keySelector(item);
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public bool Contains(TItem item)
{
return ContainsKey(GetKeyForItem(item));
}
public bool TryGetItem(TKey key, out TItem item)
{
return _dict.TryGetValue(key, out item);
}
public void Add(TItem item)
{
_dict.Add(GetKeyForItem(item), item);
}
public void AddOrUpdate(TItem item)
{
_dict[GetKeyForItem(item)] = item;
}
public bool UpdateKey(TKey oldKey, TKey newKey)
{
TItem oldItem;
if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey)) // important
return false;
RemoveKey(oldKey);
Add(oldItem);
return true;
}
private bool SetItem(TItem item, TKey key)
{
var propertyInfo = _keyInfo as PropertyInfo;
if (propertyInfo != null)
{
if (!propertyInfo.CanWrite)
return false;
propertyInfo.SetValue(item, key, null);
return true;
}
var fieldInfo = _keyInfo as FieldInfo;
if (fieldInfo != null)
{
if (fieldInfo.IsInitOnly)
return false;
fieldInfo.SetValue(item, key);
return true;
}
return false;
}
public bool RemoveKey(TKey key)
{
return _dict.Remove(key);
}
public bool Remove(TItem item)
{
return RemoveKey(GetKeyForItem(item));
}
public void Clear()
{
_dict.Clear();
}
public void CopyTo(TItem[] array, int arrayIndex)
{
Items.CopyTo(array, arrayIndex);
}
public IEnumerator<TItem> GetEnumerator()
{
return Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
I have implemented ICollection<TItem> to make it more standard compliant - and also you get the nice collection initializer syntax! :)
A sample usage:
var p1 = new Person { Name = "a" };
var p2 = new Person { Name = "b" };
var people = new KeyedCollection<string, Person>(p => p.Name) { p1, p2 };
// p1 == people["a"];
// p2 == people["b"];
C# dynamic properties post seems to show that using a Dictionary was a popular choice. The other posts suggest using a HashTable
Dictionary vs Hashtable