First of, like Olivier said above, a List<Child>
is not a subclass of List<Parent>
, even if Child
is a child of Parent
. The same goes for ObservableCollection. This subject is called variance. It goes too far to discuss it deeply here, but you could take a look at C# : Is Variance (Covariance / Contravariance) another word for Polymorphism? for more details. IEnumerable<T>
is covariant. I'm going to switch to that. If you specifically need list or observable collections, you can trivially construct them afterwards, or even feed them in.
What you would like to have is a strongly typed Get function, that returns an arbitrary IEnumerable from an ObservableCollection> where U is a child of T. Lets see if we can write that signature:
IEnumerable<U> Get<U, T>(ObservableCollection<IEnumerable<T>> source) where U : T
that's the most abstract version of what we want, but we don't actually need it to be that generic: we already know the base type at compile time. For easier reading, we can make it more specific:
IEnumerable<U> Get<U>(ObservableCollection<IEnumerable<Parent>> source) where U : Parent
Now lets fill in the body of that function:
IEnumerable<U> Get<U>(ObservableCollection<IEnumerable<Parent>> source) where U : Parent {
return source.First(inner => inner is IEnumerable<U>)
}
Wow, that was actually quite simple!
This is not really the api you want. If we put everything together, we get
ObservableCollection<IEnumerable<Parent>> ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(ManagerTemplates);
internal static List<IEnumerable<Parent>> ManagerTemplates = new List<IEnumerable<Parent>>
{
new IEnumerable<ChildA>(){},
new IEnumerable<ChildB>(){},
};
IEnumerable<U> Get<U>() where U : Parent {
return ManagerListStack.First(inner => inner is IEnumerable<U>)
}
now, we still don't have the behaviour you want: ObservableCollections
of ObservableCollections
. It turns out that we can't properly do this in C#'s type system. What we can do however, is shift the responsibility from the type system to the programmer. We keep the ObservableCollection<IEnumerable<Parent>>
, but we fill it with ObservableCollections<Child>
. This is possible because ObservableCollection<Child>
imlements IEnumerable<Child>
, and IEnumerable<Child>
is a subtype of IEnumerable<Parent>
.
ObservableCollection<IEnumerable<Parent>> ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(ManagerTemplates);
internal static List<IEnumerable<Parent>> ManagerTemplates = new List<IEnumerable<Parent>>
{
new ObservableCollection<ChildA>(){},
new ObservableCollection<ChildB>(){},
};
ObservableCollection<U> Get<U>() where U : Parent {
return (ObservableCollection<U>)ManagerListStack.First(inner => inner is ObservableCollection<U>)
}
we use the cast as an escape hatch to get around the limitations of the library that doesn't provide a co-variant ObservableCollection (which seems impossible anyway, so don't blame them for it)
The full program I used in LinqPad to test:
void Main()
{
new Test().Get<ChildA>().First().Talk();
}
class Test {
internal List<IEnumerable<Parent>> ManagerTemplates;
private ObservableCollection<IEnumerable<Parent>> ManagerListStack;
public Test() {
this.ManagerTemplates = new List<IEnumerable<Parent>>
{
new ObservableCollection<ChildA>(){ new ChildA()},
new ObservableCollection<ChildB>(){ new ChildB()},
};
this.ManagerListStack = new ObservableCollection<IEnumerable<Parent>>(this.ManagerTemplates);
}
public ObservableCollection<U> Get<U>() where U : Parent {
return (ObservableCollection<U>)ManagerListStack.FirstOrDefault(inner => inner is ObservableCollection<U>);
}
}
abstract class Parent {
abstract public void Talk();
}
class ChildA : Parent {
public override void Talk(){
Console.WriteLine("I'm an A");
}
}
class ChildB : Parent {
public override void Talk(){
Console.WriteLine("I'm a B");
}
}
As expected, it prints "I'm an A"