views:

293

answers:

2

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?

+1  A: 

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:

public IRelation
{
   public string RelationshipType { get; set; }
}

public IGenericRelation<TParent, TChild> : IRelation
{
    public TParent Parent { get; set; }
    public TChild Child { get; set; }
}

If your list contained one IGenericRelation<Foo, Bar> and one IGenericRelation<Fizz, Buzz> you can't enumerate and get both back without knowing which concrete type you are looking for:

//Theoretical, non-compiling example....
foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>>)
{ 
    //This wouldn't work for type IGenericRelation<Fizz, Buzz>
    relationControl.Parent.FooProperty = "Wibble";

    //You would be able to access this, but there is no advantage over using IRelation
    relationControl.RelationshipType = "Wibble";
}

(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.

Martin Harris
Yes, they are the input types of the generic interface.
Paul Suart
+3  A: 

This isn't possible, as IGenericRelation<T,F> is a completely distinct type from IGenericRelation<G,I>. If you need access to particular properties that are common to all IGenericRelation's, then you'll either need to implement them at the IRelation layer, or introduce a third interface between IRelation and IGenericRelation<,> 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 (either IRelation or an intermediate interface) and strongly typed at the IGenericRelation<,> level.

Adam Robinson