is GenericClass ??
I love the new generics in .Net 2.0; Combining it with abstract classes and I could almost throw my interfaces to the garbage collection of life (coming soon…).
There is one scenario though that I can’t seem to figure out the solution with a generic type, and that’s the is keyword. The problem is that you can’t ask the object if it is from GenericClass<T> type, assuming you don’t care about the T object. You can’t even ask if it is from GenericClass<BaseClass> (where T : BaseClass) in case that T is inherits from BaseClass. The only way to get “true” from this phrase it is to ask for the specific T used in the GenericClass, which is most of the time unknown. I know, I know, this is Chinese at the first read, so here is an example and than you’ll have to go Memento(the movie) on this post and roll back to the beginning of the post to understand my point:
public class EntityCollection<T> : List<T>
where T : EntityBase
{
public virtual string GetXml()
{
StringBuilder output = new StringBuilder(Count * 100);
output.Append(“<“ + typeof(T).Name + “List” + “>”);
// Build the inner xml for every “T” object.
this.ForEach(delegate(T t) { output.Append(t.GetXml()); });
output.Append(“</” + typeof(T).Name + “List” + “>”);
return output.ToString();
}
}
public abstract class EntityBase
{
public virtual string GetXml()
{
StringBuilder output = new StringBuilder(100);
Type currentType = this.GetType();
output.Append(“<“ + currentType.Name + “>”);
// —
// Use reflection to build the properties xml on the fly.
PropertyInfo[] props = currentType.GetProperties();
foreach (PropertyInfo prop in props)
{
object propertyValue = prop.GetValue(this, null);
string text = “”;
if (propertyValue != null)
{
// ****
// DON’T WORK:
// propertyValue will never be from EntityCollection<EntityBase> though it will contain EntityCollection<Item>
// and Item inherits from EntityBase. This is logically right of course, BUT I still need a workaround.
if (propertyValue is EntityCollection<EntityBase>) // <– The problem is here!
{
text = ((EntityCollection<EntityBase>)propertyValue).GetXml();
}
else if (propertyValue is EntityBase)
{
text = ((EntityBase)propertyValue).GetXml();
}
else
{
text = “<![CDATA[“ + propertyValue.ToString() + “]]>”;
}
// ****
}
output.Append(“<“ + prop.Name + “>”);
output.Append(text);
output.Append(“</” + prop.Name + “>”);
}
// —
output.Append(“</” + currentType.Name + “>”);
return output.ToString();
}
}
public class Order : EntityBase
{
private int m_orderID;
private EntityCollection<Item> m_orderItems;
public Order(int orderID)
{
m_orderID = orderID;
m_orderItems = new EntityCollection<Item>();
}
public EntityCollection<Item> OrderItems
{
get { return m_orderItems; }
set { m_orderItems = value; }
}
}
public class Item : EntityBase
{
private int m_id;
private string m_name;
public int ID
{
get { return m_id; }
set { m_id = value; }
}
public string Name
{
get { return m_name; }
set { m_name = value; }
}
public Item(int id, string name)
{
m_id = id;
m_name = name;
}
}
And here is a simple tester:
static void Main(string[] args)
{
Order o = new Order(1);
o.OrderItems.Add(new Item(1, “.Net 2.0 book”));
o.OrderItems.Add(new Item(2, “yet another .Net 2.0 book”));
string oXml = o.GetXml();
}
The only solution I found to this problem is to implement an interface and inherit from it in my generic class and than use the is keyword on my interface instead of on the GenericClass<T>, i.e:
- Create IXml interface which hold the GetXml method signature.
- The EntityBase & EntityCollection class implement the IXml interface.
- The EntityBase.Getxml() method check if propertyValue is IXml and if so – call ((IXml)propertyValue).GetXml();