I have two interfaces, a generic and a non-generic that have an inheritence hierarchy:
public interface IGenericRelation<TParent, TChild> : IRelation
public interface IRelation
The generic one is implemented by several server controls that are loaded dynamically and I wish to enumerate on the collection of controls that implement this interface. I can do the following
foreach (IRelation relationControl in this.uiPlhControls.Controls.OfType<IRelation)
{ ... }
But what I'd really like to be able to do is...
foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>)
{ ... }
and then be able to use the relationControl
with the types that it supplied as then I'd have access to the strongly-typed properties available on IGenericRelation. Unfortunately this isn't possible as it seems I can't omit the type parameters.
Does anyone know a way to enumerate the controls that implement a generic interface to prevent me having to write several loops instead of one? Using reflection perhaps?
This isn't possible, as
IGenericRelation<T,F>
is a completely distinct type fromIGenericRelation<G,I>
. If you need access to particular properties that are common to allIGenericRelation
's, then you'll either need to implement them at theIRelation
layer, or introduce a third interface betweenIRelation
andIGenericRelation<,>
that implements these. The reason for this is that the compiler has no means by which to infer what types to expect it to implement.The easiest way to go about this is to implement your two properties as an
object
at the higher level (eitherIRelation
or an intermediate interface) and strongly typed at theIGenericRelation<,>
level.Which strongly typed properties are you trying to access? If they are strongly typed because they are the input types of the generic then you won't be able to access them without supplying the types in your foreach loop. If they are strongly typed, but not related to the supplied types, can you move them to the IRelation class?
This will make more sense with a code sample - let's say that your classes are something like:
If your list contained one
IGenericRelation<Foo, Bar>
and oneIGenericRelation<Fizz, Buzz>
you can't enumerate and get both back without knowing which concrete type you are looking for:(Note that I also had to change the type of relationControl in the foreach from your example code so the possible usage makes some sense.)
Fundamentally it can be helpful to think of .NET generics the same as C++ templated classes (I know the implementation is different, but the effect in this regard is the same). Imagine that at compile time all your code is inspected for uses of the IGenericRelation class and concrete, non-generic, classes are created by performing a find for the TParent and TChild keywords and replacing them with the requested type. Since the two created classes are as separate as any two other .NET classes it makes no sense to request "all classes which started out as this template", the best you can do is look for a shared base class or interface - in this case IRelation.