views:

161

answers:

2

SHORT FORM(!)

In response the Jon Skeet's comment, what I want C# to be to do is to allow a generic, at compile time, to expand out and derive from one of it's generic parameters (as Kirk Woll demonstrates):

public class Generic<TBase> : TBase {

}

This class, of course, would not be able to override any members of TBase (unless perhaps if a constraint was applied) and would have to repeat all of TBase's public + protected constructors untouched - automatically forwarding the calls to the base.

The long form of the question shows an abstract + concrete class that presents a common pattern for implementing a given interface - but which, because C# doesn't allow multiple inheritance, cannot easily be applied on top of another base class without cloning all of the code and sticking a base class under the abstract - not exactly DRY!

LONG FORM

I know the technical reasons why the following code doesn't work - generics are run-time compiled, not compile-time templates - but I'm curious to know why the C# designers appear so reticent to go this way with the language (i've seen readability cited but struggle to understand that). I'd also like to know how you guys would implement this pattern.

I have an interface for an object that resolves dependencies via an IDependencyResolver:

public interface IDependant
{
  IDependencyResolver Resolver { get; set; }
}

I then have an abstract class

public abstract class DependantBase : IDependant
{
  #region IDependant Members

  public abstract IDependencyResolver Resolver
  {
    get; set; 
  }

  #endregion

  public virtual TDependency ResolveDependency<TDependency>(
    string name, params object[] args)
  {
    //null checks elided
    return Resolver.Resolve<TDependency>(name, args);
  }

  public TDependency ResolveDependency<TDependency>(
    params object[] args)
  {
    return ResolveDependency<TDependency>(null, args);
  }
}

And finally an instance class that simply materialises the Resolver property:

public class Dependant : DependantBase
{
  public override IDependencyResolver Resolver
  { get; set; }
}

So I can derive this type and override both the Resolver property, as well as how the resolve operation is actually carried out.

I've used it on a vanilla type (i.e. one that otherwise doesn't have a base) and then I come to write an MVC controller.

What I want to be able to do is to repeat the above two classes, but have them implemented as follows:

//'injects' the DependantBase functionality
//between TBase and the deriving class
public abstract class DependantBase<TBase> : TBase, IDependant
{
  //as DependantBase above
}

//and then have Dependant<TBase>:
public class Dependant : DependantBase<TBase>
{ 
  //as Dependant above
}

Thus, if the above were possible I could simply do this:

public class HomeController : Dependant<System.Web.Mvc.Controller>
{

}

And now HomeController derives from Dependant<System.Web.Mvc.Controller>, which in turn inherits from System.Web.Mvc.Controller.

The key here being that I want to preserve the previous pattern of being able to override both the property and the ResolveDependency helper method - but I don't want to have to keep cloning the same infrastructure code for each new branch of types (i.e. where a new pre-existing base type is required by some framework).

This is clearly not a generic in the .Net term because I'm not sure how you'd even begin to represent this in metadata - but the C# compiler could certainly treat this is as a template instead and expand it out at compile-time (god, it does it enough of this already with Expression Trees, DLR interfacing, in-line delegates, iterator blocks and auto get/set properties!).

The only way I can see of doing something like this is to push it up into a Visual Studio plugin (new language extension or something like that), or even a text template.

+2  A: 

You could potentially accomplish your goals by using T4. This is already built into Visual Studio, and works with C# today.

Reed Copsey
Yes, I know - I mention that at the very end of the question. But requiring my development team to do a registry hack to set shared T4 template locations, and then include a T4 file that doesn't reproduce it's output unless you tell it to just seems a very long way around to go! Don't get me wrong, though I love T4 and have used it to do some v.cool things...
Andras Zoltan
@Andras: Well, you mentioned that the studio team "could have" done things this way - I just wanted to point out that they did, if you want to use the tools. As for the registry "hack" - You should be able to use T4, if you set it up properly, without that. Many ORMs use this very effectively, for example.
Reed Copsey
@Reed Copsey - fair point :)
Andras Zoltan
@Andras, not sure what you mean by a "registry hack". Your T4 template should be yet another file in your .csproj project. And by default, it regenerates all the time (when you save, build, etc.), so not sure what you mean by only doing so when you tell it to.
Kirk Woll
@Kirk: Yeah - I was kind of confused by that, as well - T4 is pretty straightforward if you set it up properly.
Reed Copsey
@Reed and @Kirk: I would want this class/pattern to be reusable in many projects. Therefore you can't just have one tt file you need one in copy in each project, which means a copy paste job. The registry hack is if you want to have the equivalent of word workgroup template folder so that you have one tt and you just include it.
Andras Zoltan
A: 

So, basically, you want to wrap a type that needs dependencies in a generic class that will derive from its own generic type and implement a method that injects the dependencies into the base type. Obviously the TBase generic parameter as a parent is illegal; you cannot create a type with a parent that is not known at design-time. C# is a strong statically-typed language. Basically, what you want to do would require dynamic or "duck-typing"; the runtime would examine a potential use of an object, interrogate the object itself, and determine if the use is valid based on the "if it looks like a duck, quacks like a duck..." philosophy. C#4.0 can do this in certain cases using the new "dynamic" keyword, but "dynamic" should not be thrown around lightly as it pretty much disconnects all the type-checking the compiler can do for you.

If you're in love with using a generic for the purpose, you could get close to this by abstracting a base type to be used as a constraint of TBase:

public class DependantBase<TBase> : DependencyInjectableBase, IDependant where TBase: DependencyInjectableBase
{
...
}

Now DependantBase is both derived from and works with DependencyInjectableBase (which can be an empty "label" parent if there is no common functionality). However, you cannot declare a new DependantBase<ChildOfDependencyInjectableBase>() and treat the DependantBase instance as the derived ChildOfDependencyInjectableBase. This is a part of basic inheritance; a class cannot be treated as its "sibling" or "cousin".

The method provided by the C# language spec for "mixins" (functionality not defined in the class itself but that can be applied as if it were) is "extension methods"; public static methods that specify the first parameter as the type being extended using the "this" contextual keyword. You could implement such a method or methods on the types you want to inject dependencies into:

public static InjectDependencies(this Object dependant)
{
   //reflectively examine properties of dependant's true type for dependency types
   //the resolver knows how to inject
}

This could then be applied to any Object. The GetType() method is available on any .NET Object, and from there you can interrogate the types of properties or fields to find dependencies you can resolve.

KeithS
An interesting solution, but I've got the act of assigning dynamic dependencies in ctor args and properties down nicely using extensive expression tree generation driven from xml. Equally I am aware I can simply assign an extension method for the IDependant interface, but then I can't do the overriding. It's not really the dependency bit that's crucial here, but the the base class pattern and the fact that it can't be applied on top of other base classes without a copy paste operation. Whereas a compile time generic would be very clean and easy to understand.
Andras Zoltan