OK, so here is the situation. I've got a FlexCollection<T>
class, which purpose is to hold a list of some specialization of FlexItem
, therefore:
public class FlexCollection<T> where T : FlexItem, new()
{
public void Add(T item) { ... }
...
}
FlexItem
is not generic class itself. What I wanted to achieve is ability to hold in FlexItem
's field a reference to the collection that contains the object. Unfortunately in C# it is not possible to hold reference to "any" specialization of template class (as in Java). At first I tried to use non-generic interface IFlexCollection
but it actually forced me to implement each method twice, i.e.:
public class FlexCollection<T> : IFlexCollection where T : FlexItem, new()
{
public void Add(T item) { ... } // to use in generic calls
public void Add(object item) { ... } // to use for calls from within FlexItem
...
}
Then I had found out that I could make FlexItem a generic class itself! Then a specialization can hold a reference to collection of objects of this specialization (which is quite logical). Therefore:
public class FlexItem<T> where T : FlexItem<T>, new()
{
public FlexCollection<T> ReferenceToParentCollection;
...
}
public class FlexCollection<T> where T : FlexItem<T>, new()
{
public void Add(T item) { ... }
...
}
Now i can declare some FlexItem<T>
specialization and corresponding collection:
public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };
The problem arises when I try to extend those classes to hold additional fields. I.e. I wanted an ExtendedItem
class which holds field B
in addition to field A
:
public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };
And the thing is that ExtendedItem
is a subclass of FlexItem<BasicItem>
and not FlexItem<ExtendedItem>
. Therefore is is impossible to declare ExtendedCollection
as above. This causes a compilation error:
The type 'Demo.ExtendedItem' must be convertible to
'Demo.FlexItem<Demo.ExtendedItem>' in order to use it as parameter 'T'
in the generic type or method 'Demo.BasicCollection<T>'
Is there any way to avoid such type collision?
OK, i've decided to give C# events a try. This way I actually do not need FlexItem to hold a reference to owning FlexCollection. I simply raise an event i.e. "IAmBeingRemoved" and FlexCollection's code if performing corresponding actions. Code logic is even better encapsulated then. I just hope I will not run into any performance issues or other generic-based suprises. :-) I am posting it so that maybe somebody finds it a good solution for their own problem.
There are 2 ways you can resolve this:
You can use the base Polymorphic Collection and not inherit the base class collection:
class Program
{
static void Main(string[] args)
{
BasicCollection extendedCollection = new BasicCollection();
extendedCollection.Add(new ExtendedItem { A = 1, B = 2});
extendedCollection.Add(new BasicItem { A = 3 });
extendedCollection.Add(new ExtendedItem { A = 4, B = 5});
foreach (BasicItem item in extendedCollection)
{
switch(item.GetType().Name)
{
case "BasicItem":
Console.Out.WriteLine(string.Format("Found BasicItem: A={0}", item.A));
break;
case "ExtendedItem":
ExtendedItem extendedItem = item as ExtendedItem;
Console.Out.WriteLine(string.Format("Found ExtendedItem: A={0} B={1}", extendedItem.A, extendedItem.B));
break;
}
}
}
}
public class FlexItem<T> where T : FlexItem<T>, new()
{
public FlexCollection<BasicItem> ReferenceToParentCollection;
}
public class FlexCollection<T> where T : FlexItem<T>, new()
{
public void Add(T item) { }
}
public class BasicItem : FlexItem<BasicItem> { public int A; }
public class ExtendedItem : BasicItem { public int B; }
public class BasicCollection : FlexCollection<BasicItem>
{
Collection<BasicItem> items = new Collection<BasicItem>();
public void Add(BasicItem item)
{
item.ReferenceToParentCollection = this;
items.Add(item);
}
public void Remove(BasicItem item)
{
item.ReferenceToParentCollection = null;
items.Remove(item);
}
public IEnumerator GetEnumerator()
{
return items.GetEnumerator();
}
}
Or you can box the collection class reference and unbox it when you need it since you know the type of the child object?
class Program
{
static void Main(string[] args)
{
ExtendedCollection extendedCollection = new ExtendedCollection();
extendedCollection.Add(new ExtendedItem { A = 1, B = 2, ReferenceToParentCollection = extendedCollection });
extendedCollection.Add(new ExtendedItem { A = 3, B = 3, ReferenceToParentCollection = extendedCollection });
foreach (ExtendedItem item in extendedCollection)
{
(item.ReferenceToParentCollection as ExtendedCollection) ...
}
}
}
public class FlexItem<T> where T : FlexItem<T>, new()
{
public object ReferenceToParentCollection;
}
public class FlexCollection<T> where T : FlexItem<T>, new()
{
public void Add(T item) { }
}
public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };
public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With