SYSK 264: Restricting Item Types in Generic Collections
Say, you have implemented the following data class called Message in a YourCompany.DataContracts.Shared assembly (code below).
When creating a collection of messages class, some may choose to implement it similar to the code below:
[DataContract(Namespace = ""), Serializable]
public class MessageCollection : System.Collections.Generic.SortedDictionary<DateTime, Message>
{
}
The problem with this approach is that if you created a class that derives from the Message class, let’s call it MessageEx, and you want a collection of the MessageEx classes, you’ll either have to create a new collection class (e.g. MessageExCollection), or you’ll end up writing a lot of code casting the collection items to the MessageEx type.
A better, in my opinion, choice is to create a generic collection, and restrict the type of items that are allowed to be inserted into such collection. Here is what I mean:
#region MessageCollection
[DataContract(Namespace = ""), Serializable]
public class MessageCollection<T> : System.Collections.Generic.SortedDictionary<DateTime, T> where T : Message
{
public void Add(T item)
{
base.Add(item.CreatedOn, item);
}
public T this[long recordID]
{
get
{
T result = default(T);
foreach (T item in base.Values)
{
if (item.RecordID == recordID)
{
result = item;
break;
}
}
return result;
}
}
// TODO: add other methods/properties
// (e.g. get by instance id, get by index, etc)
}
#endregion
Now, you can have a Message class and MessageEx class, and instanced of both classes can be inserted into MessageCollection. And yes, no casting required when inserting or retrieving and using the message item!
[DataContract(Namespace = ""), Serializable]
public class MessageEx : YourCompany.DataContracts.Shared.Message
{
private string _someOtherData;
[DataMember]
public string SomeOtherData
{
get { return _someOtherData; }
set { _someOtherData = value; }
}
}
namespace YourCompany.DataContracts.Shared
{
#region Message
[DataContract(Namespace = ""), Serializable]
public class Message
{
private string _instanceID = Guid.NewGuid().ToString();
private long _recordID;
private string _text;
private DateTime _createdOn;
private string _createdBy;
public Message()
{
}
public Message(long recordId, string text, DateTime createdOn, string createdBy)
{
_recordID = recordId;
_text = text;
_createdOn = createdOn;
_createdBy = createdBy;
}
[DataMember]
public string InstanceID
{
get { return _instanceID; }
set { _instanceID = value; }
}
[DataMember]
public long RecordID
{
get { return _recordID; }
set { _recordID = value; }
}
[DataMember]
public string Text
{
get { return _text; }
set { _text = value; }
}
[DataMember]
public DateTime CreatedOn
{
get { return _createdOn; }
set { _createdOn = value; }
}
[DataMember]
public string CreatedBy
{
get { return _createdBy; }
set { _createdBy = value; }
}
}
#endregion
[DataContract(Namespace = ""), Serializable]
public class MessageEx : YourCompany.DataContracts.Shared.Message
{
private string _someOtherData;
[DataMember]
public string SomeOtherData
{
get { return _someOtherData; }
set { _someOtherData = value; }
}
}
Usage:
MessageCollection<MessageEx> messages =
new MessageCollection<MessageEx>();
. . .
// No casting required :)
MessageEx message = messages[recordID];