What are the good reasons to wish that .NET generics could inherit one of the generic parameter types?

Viewed 1843

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 give my reason to have it as an answer to this question - see below.

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.

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.

8 Answers

I'm using GTK# 3. There's a 'Widget' class defined which is the base for all other GTK# widgets, e.g. Window, Label, Frame. Unfortunately, the framework makes it a bit complicated to change the background-color of a widget, in the sense that you need to override a method of the Widget to do it (jeez...).

So, if I wanted to have - say - a Label for which I can arbitrarily set a background-color, I should do something like this:

class BGLabel : Label
{
    private Color _bgColor;

    public Color BackgroundColor
    {
        get { return this._bgColor; }
        set { this._bgColor = value; this.QueueDraw(); /* triggers OnDraw */ }
    }

    protected override void OnDraw(...)
    {
        ... /* here we can use _bgColor to paint the background */
    }
}

Now, if I wanted to have this nice feature for more widgets, I'd have to do the above for each of them. If "class C< T > : T" would be compilable, I could instead just do:

class C<T> : T where T : Widget
{
    private Color _bgColor;

    public Color BackgroundColor
    {
        get { return this._bgColor; }
        set { this._bgColor = value; this.QueueDraw(); /* triggers OnDraw */ }
    }

    protected override void OnDraw(...)
    {
        ... /* here we can use _bgColor to paint the background */
    }
}

and instead of "BGxxx bgw = new BGxxx();" I could use "C< xxx > bgw = new C< xxx >();".

Once when I wanted this was when I had database objects (e.g. MyEntity) that also had a corresponding history object (e.g. MyEntityHistory). These entities shared exactly the same properties, except for 2 that were only present on the history one: ValidFrom and ValidTo.

So, in order to display the current state of an object as well as history data, I could fetch this writing UNION between them and use mapping capabilities of Dapper or EF Core's FromSql to map the result to List<MyEntityHistory> history.

Of course I would like to avoid duplicating the properties, so Ideally I'd inherit the entity class to get it's properties. But also, I'd like to get the ValidFrom and ValidTo properties from some other base class, so I'd end up with a diamond problem. What would help, is that I'd have a history class defined like this:

class MyEntityHistory : HistoryEntity<MyEntity>

Where HistoryEntity would be defined as such:

class HistoryEntity<TEntity> : TEntity where TEntity: class
{
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
}

Unfortunately such implementation of HistoryEntity<TEntity> is not possible.

Note that embedding entity class (MyEntity) through composition is not an option, because Dapper or EF Core's mapper wouldn't be able to work around the nested object.

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.

I have written CodeFirstWebFramework, which is a dll providing all the tools to write a database driven web server, with the tables automatically generated from classes in the code, and gluing urls to calls to methods in AppModule classes.

The DLL provides some base AppModule classes - AdminModule and FileSender. These implement administration (logging on, maintaining users, updating settings), and returning files when the url does not relate to any other AppModule.

Consumers of the DLL can override the abstract AppModule class to add additional functionality to all their own AppModules (which will be derived from the override).

I would also like them to be able to override AdminModule and/or FileSender. However it would be useful (and, in some cases, essential) for them to be able to override one of these modules (thus having access to the default code therein), while also subclassing their own AppModule implementation.

I thought I had had a brilliant idea, as follows:

In my DLL:

abstract class AppModule {
    // Contains base class implementation, including virtual Database property, code to 
    // retrieve files, code to call appropriate method depending on url, code to collect 
    // result and return it to the web browser, etc.
}

abstract class AdminModule<T> where T:AppModule, new() : T {
    // Contains implementation of Admin methods - create/edit users, 
    // edit settings, backup database, etc.
}

class AdminModule : AdminModule<AppModule> {
    // No code needed - inherits implementation from AdminModule<AppModule>
}

In the consumer program (different Namespace):

abstract class AppModule : AppModule {
    // Contains additional properties and methods. Also overides some virtual methods,
    // e.g. returns a subclass of Database with additional methods, or retrieve files
    // from the database instead of the file system.
}

class AdminModule : AdminModule<AppModule> {
    // Additional code for Admin methods specific to this application
    // Does not need code for methods in base class - inherits implementation from 
    // AdminModule<AppModule>
}

In this particular instance, because of the constraint that T is an AppModule, and because AdminModule<T> is itself abstract, I can't see any reason why any of the objections raised so far would apply.

The compiler could check that any abstract methods were implemented in derived classes. There would be no danger of constructing an AdminModule<T>, because it is abstract.

It would be a compile-time error to write AdminModule<T> if T was a sealed class, or derived from AdminModule<T>, or has less accessibility than AdminModule<T>.

The good reason to make it possible to inherit from a type parameter is because of the following situation:

I have would like to have an abstract ViewModel class which inherits from an Entity class and has some additional properties and methods.

public abstract class ViewModel<TEntity> : TEntity, IViewModel<TEntity> 
where TEntity : class, IEntity, new() {
    public void SomeMethod() {

    }
}

Then i could make a specific ViewModel

public class EmployeeViewModel : ViewModel<Employee> {

}

Nice ! it inherits all the fields of the entity and has the standard properties and methods of the abstract viewmodel ! ..

This sadly cannot be done now.

Related