views:

51

answers:

3

I've been dealing a lot lately with abstract classes that use generics. This is all good and fine because I get a lot of utility out of these classes but now it's making for some rather ugly code down the line. For example:

abstract class ClassBase<T>
{
    T Property { get; set; }
}

class MyClass : ClassBase<string>
{
    OtherClass PropertyDetail { get; set; }
}

This implementation isn't all that crazy, except when I want to reference the abstract class from a helper class and then I have to make a list of generics just to make reference to the implemented class, like this below.

class Helper
{
    void HelpMe<C, T>(object Value)
        where C : ClassBase<T>, new()
    {
        DoWork();
    }
}

This is just a tame example, because I have some method calls where the list of where clauses end up being 5 or 6 lines long to handle all of the generic data. What I'd really like to do is

class Helper
{
    void HelpMe<C>(object Value)
        where C : ClassBase, new()
    {
        DoWork();
    }
}

but it obviously won't compile. I want to reference ClassBase without having to pass it a whole array of generic classes to get the function to work, but I don't want to reference the higher level classes because there are a dozen of those. Am I the victim of my own cleverness or is there an avenue that I haven't considered yet?

+2  A: 

I suppose that your HelpMe method would be used for initializing the concrete ClassBase<T> type (a guess based on the constraints). To keep the code fully generic (if you need both T and C somewhere in the method), you probably need to keep both of the type parameters.

However, you could add a non-generic base class and then write something like this:

abstract class ClassBase { 
   object UntypedProperty { get; set; } 
} 
abstract class ClassBase<T> : ClassBase { 
   T Property { get; set; } 
   public override object UntypedProperty { 
     get { return Property; }
     set { Property = (T)value; }
   }
} 

Then you could be to write the helper method like this:

void HelpMe<C>(object Value) where C : ClassBase, new() { 
  var n = new C();
  c.UntypedProperty = Value;
} 

Depending on your specific scenario, something along these lines might work and make the code a little bit simpler. However, you need to modify the base class to make this possible.

Tomas Petricek
A: 

Generics do have a tendency to propogate through the code, and they're seldomly used as "mixin" classes for that reason.

Thomas mentioned the one possibility of introducing an equivalent non-generic API. I would prefer revisiting the design and making these base classes non-generic if possible while maintaining full type safety. Whether or not this is actually possible is determined by your requirements.

There is one other possibility short of a re-design (or API duplication): dynamic. If you're willing to lose IntelliSense in your helper methods (and are willing to pay a very, very small runtime performance penalty), you can use dynamic in your helper method:

class Helper
{
    void HelpMe<C>(object Value)
      //  where C : ClassBase<T>, new() // no longer needed, but should be documented
    {
        dynamic cObj = Activator.CreateInstance<C>(); // instead of "new C()"
        cObj.PropertyDetail = ...;
        cObj.Property = ...;
        ...
    }
}
Stephen Cleary
A: 

without having to pass it a whole array of generic classes to get the function to work

A small change might ease these calls. Move repeatedly specified types to the generic declaration of the class.

  //before
Helper x = new Helper();
x.HelpMe<MyClass, string>(x);
x.HelpMe<MyClass, string>(y);

  //after
Helper x = new Helper<string>();
x.HelpMe<MyClass>(x);
x.HelpMe<MyClass>(y);



  //the change
class Helper<T>
{ 
    void HelpMe<C>(object Value) 
        where C : ClassBase<T>, new() 
    { 
        DoWork(); 
    } 
} 
David B