I'm running into an issue where I have one generic that I'm trying to cast to another where the second's generic T parameter is a subclass of the one used in the first.
Here's my code, simplified for understanding...
public partial class HierarchicalItem
{
public ObservableHierarchicalCollection<HierarchicalItem> ContainingCollection{ get; private set; }
public HierarchicalItem Parent{ get{
return (ContainingCollection != null)
? ContainingCollection.Owner
: null;
}}
}
public partial class HierarchicalItem
{
public class ObservableHierarchicalCollection<T> : ObservableCollection<T>
where T : HierarchicalItem
{
public ObservableHierarchicalCollection(HierarchicalItem owner)
{
this.Owner = owner;
}
public HierarchicalItem Owner{ get; private set; }
private void ExistingMemberCheck(T item)
{
if(item.ContainingCollection != null) throw new ExistingMemberException();
item.ContainingCollection = this; // <-- This fails because of casting
}
protected override void InsertItem(int index, T item)
{
ExistingMemberCheck(item);
base.InsertItem(index, item);
}
protected override void SetItem(int index, T item)
{
CheckParent(item);
// Get the item and unhook the hierarchy
var existingItem = this[index];
existingItem.ContainingCollection = null;
base.SetItem(index, item);
}
protected override void RemoveItem(int index)
{
// Get the item and unhook the hierarchy
var existingItem = this[index];
existingItem.ContainingCollection = null;
base.RemoveItem(index);
}
}
}
So how do I get around this?
In your example, the
ObservableHierarchicalCollection<T>
is only ever instantiated withT = HierarchicalItem
. If that will always be the case, you could try making the collection non-generic and inheriting fromObservableCollection<HierarchicalItem>
instead.If not, you may be able to change
HierarchicalItem
to also be abstract and generic, (HierarchicalItem<T> where T : HierarchicalItem<T>
) like IComparable and similar interfaces do. You would then haveDerivedItem : HierarchicalItem<DerivedItem>
, which would have a ContainingCollection of type ObservableHierarchicalCollection. Note that this could make mixing item types difficult.C# 4.0 supports explicit co-variance and contra-variance.
You could use the out keyword in the ObservableCollection interface declaration:
And then the interface will be co-variant.
more about it:
Covariance and Contravariance (C# and Visual Basic)
Covariance and Contravariance FAQ
How is Generic Covariance & Contra-variance Implemented in C# 4.0?
Switch the top part to something like this:
You have to use the non-generic interface because, as Nicholas, states co-variance is your enemy in this instance.