tags:

views:

168

answers:

2

This post is in continuation of this one.

I am trying to understand if I am the only one who misses and needs the ability of a .NET generic type to inherit one of its generic parameter types.

The challenge is to collect compelling reasons in favour of this feature or, alternatively, get to know that there are none.

I will start with mine and I am asking folks out there to add theirs as answers to this post.

If you disagree that the feature is useful or have no good reasons in favour - please refrain from posting anything here, though you can do so in the original post that has started it all - here.

Now my reason. Every now and then I stumble upon an implementation issue, where I deeply regret that C<T> cannot inherit T. Unfortunately, I have never recorded these issues and so I can describe just the most recent one - the one I have stumbled upon right now. So here it is:

Our system is extensible through metadata, which becomes available at run-time. The metadata is translated to a type dynamically generated at run-time using Reflection.Emit. Unfortunately, the dynamically generated type has to satisfy the following conditions:

  1. It must derive from some other type, provided as a parameter to the dynamic type creator. This type is called the ancestor type and is always a statically compiled type.
  2. It must implement several interfaces, say IDynamicObject (our interface), System.ComponentModel.INotifyPropertyChanged and Csla.Core.IMobileObject. Note, that this condition places certain constraints on the ancestor type. For instance, the ancestor type may not implement the IDynamicObject interface, except if all the interface methods are implemented abstractly. There are other constraints, all of which must be checked.
  3. It should (and it does) override the object methods Equals, ToString and GetHashCode.

Currently, I had to use Reflection.Emit to emit all the IL code to satisfy these conditions. Of course, some tasks may be forwarded to statically compiled code. For instance, the override of the object.Equals(object) method invokes DynamicObjectHelper(IDynamicObject, IDynamicObject) which is a statically compiled C# code doing the largest bulk of the work of comparing two dynamic objects for equality. But this is more of an exception than the rule - most of the implementation is emitted, which is a pain in the butt.

Would not it be great to be able to write a generic type, like DynamicObjectBase<T> which will be instantiated with the ancestor type and serve as the actual base type of the dynamically generated type? As I view it, the generic type DynamicObjectBase<T> could implement much of the dynamic type requirements in a statically compiled C# code. The dynamically emitted type would inherit it and probably, just override a few simple virtual methods.

To conclude, my compelling reason is that letting C<T> inherit from T would greatly simplify the task of emitting dynamic types.

P.S.

Some C++ patterns are irrelevant in .NET. For instance, in his excellent book Modern C++ Design Andrei Alexandrescu describes how to create a list of types evaluated at the compile time. Naturally, this pattern is irrelevant for .NET, where if I need a list of types I just create List<Type> and populate it with types. So, let us try to come up with reasons pertinent to the .NET framework and not just blindly translating C++ coding techniques to C#.

P.P.S.

Of course, this discussion is strictly academic. Even if a hundred compelling reasons for the feature in question is surfaced it is not going to be implemented, ever.

A: 

While I see what you're getting at, it looks like a special case of a more general problem of poor support for type construction via composition in .NET. Would something like the approach described at https://connect.microsoft.com/VisualStudio/feedback/details/526307/add-automatic-generation-of-interface-implementation-via-implementing-member be sufficient for you?

Nicole Calinoiu
Well, it depends. If Microsoft adds it at the language level, then this is of no use to scenarios involving dynamically emitted code. But otherwise, yes, this would be of great help. I have up-voted the issue.
mark
+1  A: 

The basic rule of generics that prevents this is "the content of a generic type must be well-defined on the generic argument." Lets look at how this applies to the following code:

public abstract class AbstractBase
{
    public abstract string MyMethod();
}

public class SomeType<T> : T
{
}

public class SomeUsage
{
    void Foo()
    {
        // SomeType<AbstractBase> does not implement AbstractBase.MyMethod
        SomeType<AbstractBase> b = new SomeType<AbstractBase>();
    }
}

So we try to implement MyMethod():

public class SomeType<T> : T
{
    public override string MyMethod()
    {
        return "Some return value";
    }
}

public class SomeUsage
{
    void Foo()
    {
        // SomeType<string> does not inherit a virtual method MyMethod()
        SomeType<string> b = new SomeType<string>();
    }
}

So lets make a requirement that T be derived from AbstractBase:

public abstract class DerivedAbstractBase : AbstractBase
{
    public abstract string AnotherMethod();
}

public class SomeType<T> : T
    where T : AbstractBase
{
    public override string MyMethod()
    {
        return "Some return value";
    }
}

public class SomeUsage
{
    void Foo()
    {
        // SomeType<DerivedAbstractBase> does not implement DerivedAbstractBase.AnotherMethod()
        SomeType<DerivedAbstractBase> b = new SomeType<DerivedAbstractBase>();
    }
}

Summary:

By the time you account for all the restrictions in base types, you're so constrained that deriving from a generic parameter is pointless.

280Z28
I really do not see the problem here. When one writes SomeType<AbstractBase> b = new SomeType<AbstractBase>();It should be interpreted by the compiler as an attempt to instantiate an abstract type - bang, compilation fails. One could, of course, derive from SomeType<AbstractBase>, and then be forced by the compiler to implement the abstract API of SomeType<AbstractBase>, which includes the abstract API of AbstractBase.
mark
@mark: The compiler does not enforce this - the CLI (the underlying virtual machine) does.
280Z28
You are correct, of course. Still, that does not change the fact that CLI will have no problems of enforcing proper semantics on the SomeType<AbstractBase> type - it will happily refuse to instantiate it.
mark