Generic constraints

B

Brian Richards

I'm trying to write a generic function (List<TPanelType>
GetGenericPanels<TPanelType, TObjectType>()) that returns all UserControls
that derive from T and and implement an interface IGenericPropertyPanel<V>
such that U implements or derives from V. The idea being that I can discover
(via reflection) all the controls that display or allow editing of an object
properties, it's base classes and interfaces. Then I can put them in some
kind of flow layout or tabcontrol.

I have a working version (code below) however I don't think that my
methodology for ensuring that a type meets all the constraints of a generic
is good. The problem is trying to make sure that TPanelType implements
IGenericPropertyPanel<> for a class or interface that is relivent for U. Can
anyone thing of a better method of doing this? Or does anyone thing I'm just
going way overboard?

Note that I did try making base class something like class MyControl<T> :
UserControl, IGenericPropertyPanel<T> where T : SomeClass, SomeInterface and
deriving controls from there because I think the discovery process would be
simpler. Except that the designer does not support forms derived from
generic forms.

Thanks,

Brian

interface I {}
interface IGenericPropertyPanel<T> { }
class A {}
class B : A, I {}
class AControl : UserControl, IGenericPropertyPanel<A>{}
class IControl : UserControl, IGenericPropertyPanel<I>{}

GetGenericPanels<UserControl, B>(typeof(B).Assembly)
returns => List<UserControl> { new AControl(), new IControl() };


public static List<TPanelType> GetGenericPanels<TPanelType,
TObjectType>(Assembly assm)
where TPanelType : class, new()
{
List<TPanelType> panels = new List<TPanelType>();
List<Type> types = new List<Type>(assm.GetTypes());

Predicate<Type> findPanelTPanelType = delegate(Type type)
{
bool result = false;

if (typeof(TPanelType).IsAssignableFrom(type))
{
result = true;
}

return result;
};

foreach (Type type in types.FindAll(findPanelTPanelType))
{
bool addPanel = false;

List<Type> interfaces = new List<Type>(type.GetInterfaces());

if (interfaces.Contains(typeof(IGenericPropertyPanel<TObjectType>)))
{
addPanel = true;
}
else
{
foreach (Type interfaceType in interfaces)
{
if ((interfaceType.IsGenericType)
&& (typeof(IGenericPropertyPanel<>) ==
interfaceType.GetGenericTypeDefinition()))
{
foreach (Type constraint in
interfaceType.GetGenericParameterConstraints())
{
if (constraint.IsAssignableFrom(typeof(TObjectType)))
{
addPanel = true;
}
}
}
}
}

if (addPanel)
{
panels.Add(Activator.CreateInstance(type) as TPanelType);
}
}

return panels;
}
 
N

Nicholas Paldino [.NET/C# MVP]

Brian,

Why not go the other way with it? Since you are using reflection, why
not have an attribute that the types attach to themselves (or have an
attribute on the assembly level that exposes the types that the assembly
wants to expose) and then get the type from that. Then, all you have to do
is create it.

The assumption would be that the type is responsible for saying "hey, I
conform to what you are looking for" and you take it's word for it.

Additionally, you now have an easy switch to turn off a panel if you
don't want it, or give the choice to those that expose these types that
happen to conform to your pattern. It also helps to solidfy the
relationship and the intent of your panel types.

Hope this helps.
 
B

Brian Richards

The problem with the attribute approach is that I don't get compile time
checking on the panel type signatures (IGenericPropertyPanel<T>). And I need
those signatures in order to set the display object of the control.
Additionally I was looking to extend the method to be able to get only the
most specific control for a given type. So that if I have
class A
class B : A
class C : A
class D : B
class ControlA : IGenericPropertyPanel<A>
class ControlB : IGenericPropertyPanel<B>

GetPropertyPanel<Panel, A> => ControlA
GetPropertyPanel<Panel, B> => ControlB
GetPropertyPanel<Panel, C> => ControlA
GetPropertyPanel<Panel, D> => ControlB

Thanks for your input.

-Brian

Nicholas Paldino said:
Brian,

Why not go the other way with it? Since you are using reflection, why
not have an attribute that the types attach to themselves (or have an
attribute on the assembly level that exposes the types that the assembly
wants to expose) and then get the type from that. Then, all you have to do
is create it.

The assumption would be that the type is responsible for saying "hey, I
conform to what you are looking for" and you take it's word for it.

Additionally, you now have an easy switch to turn off a panel if you
don't want it, or give the choice to those that expose these types that
happen to conform to your pattern. It also helps to solidfy the
relationship and the intent of your panel types.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Brian Richards said:
I'm trying to write a generic function (List<TPanelType>
GetGenericPanels<TPanelType, TObjectType>()) that returns all UserControls
that derive from T and and implement an interface
IGenericPropertyPanel said:
such that U implements or derives from V. The idea being that I can
discover
(via reflection) all the controls that display or allow editing of an
object
properties, it's base classes and interfaces. Then I can put them in some
kind of flow layout or tabcontrol.

I have a working version (code below) however I don't think that my
methodology for ensuring that a type meets all the constraints of a
generic
is good. The problem is trying to make sure that TPanelType implements
IGenericPropertyPanel<> for a class or interface that is relivent for U.
Can
anyone thing of a better method of doing this? Or does anyone thing I'm
just
going way overboard?

Note that I did try making base class something like class MyControl<T> :
UserControl, IGenericPropertyPanel<T> where T : SomeClass, SomeInterface
and
deriving controls from there because I think the discovery process would
be
simpler. Except that the designer does not support forms derived from
generic forms.

Thanks,

Brian

interface I {}
interface IGenericPropertyPanel<T> { }
class A {}
class B : A, I {}
class AControl : UserControl, IGenericPropertyPanel<A>{}
class IControl : UserControl, IGenericPropertyPanel<I>{}

GetGenericPanels<UserControl, B>(typeof(B).Assembly)
returns => List<UserControl> { new AControl(), new IControl() };


public static List<TPanelType> GetGenericPanels<TPanelType,
TObjectType>(Assembly assm)
where TPanelType : class, new()
{
List<TPanelType> panels = new List<TPanelType>();
List<Type> types = new List<Type>(assm.GetTypes());

Predicate<Type> findPanelTPanelType = delegate(Type type)
{
bool result = false;

if (typeof(TPanelType).IsAssignableFrom(type))
{
result = true;
}

return result;
};

foreach (Type type in types.FindAll(findPanelTPanelType))
{
bool addPanel = false;

List<Type> interfaces = new List<Type>(type.GetInterfaces());

if (interfaces.Contains(typeof(IGenericPropertyPanel<TObjectType>)))
{
addPanel = true;
}
else
{
foreach (Type interfaceType in interfaces)
{
if ((interfaceType.IsGenericType)
&& (typeof(IGenericPropertyPanel<>) ==
interfaceType.GetGenericTypeDefinition()))
{
foreach (Type constraint in
interfaceType.GetGenericParameterConstraints())
{
if (constraint.IsAssignableFrom(typeof(TObjectType)))
{
addPanel = true;
}
}
}
}
}

if (addPanel)
{
panels.Add(Activator.CreateInstance(type) as TPanelType);
}
}

return panels;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top