user650261
user650261

Reputation: 2265

Inheriting generics with inherited parameterizations

I am looking for a way to inherit generics with inherited parameterizations - or if that's not possible, the closest way to get the same functionality.

Consider the following:

Class B inherits from class A

Class D inherits from Class C

Now, I have one class:

abstract class A<T> where T : C

with constructor:

public A(T t)

Now, I wish to extend class A as so:

class B<D> : A<C>

Creating a constructor:

 public B(D t) : base(t){ /*stuff here*/}

However, this throws a compile-time error, since D is not C. So my two questions are:

a) Is there a clean way to do this? In the worst case scenario, I think I can replace D with C with little problem, but maybe there's a reason why that's not a safe idea?

b) Should I even be explicitly stating my generics in the child class definition, or is there a cleaner way I should do this with constrained types?

Upvotes: 6

Views: 98

Answers (1)

Tyree Jackson
Tyree Jackson

Reputation: 2608

Add a where constraint to class B

class B<D> : A<C> where D : C

To answer you questions:

a) With the fix in this answer, your approach is pretty clean so far, if a tad over abstract (due to the names mostly).

b) You should only be adding the D generic type parameter to your child class B if the child class itself is either adding value in a generic way related to subclasses of C which are instances of D; or if the child class is itself incomplete, is expected to be extended and requires knowledge subclasses of C as D. In which case, you should mark it abstract.

UPDATE:

I wanted to add one more thing about this. In the signature above, the T parameter of A<T> will be C in those members of A that use the T type. This may be a problem as demonstrated in the following example:

public class C {}

public class F : C {}

public class E : C {}

public class A<T> where T : C
{
    protected T cSubclass;
    public void SetCSubclass(T cSubclass) { this.cSubclass = cSubclass; }
}

public class B<D> : A<C> where D : C
{
    public D GetCSubclass()
    {
        return this.cSubclass;
    }
}

The code in this example will not compile. You will get the following compilation error:

error CS0266: Cannot implicitly convert type 'C' to 'D'. An explicit conversion exists (are you missing a cast?)

However the compilation error is resolved if we change class B to the following:

public class B<D> : A<D> where D : C
{
    public D GetCSubclass()
    {
        return this.cSubclass;
    }
}

The reason is that D is to be a specific subclass of C, however in the former version we've only constrained A to any form of C including itself and any of its subclasses. Therefore we could potentially call new B<F>.SetCSubclass(new E()); which would be a different type then what GetCSubclass would be expecting to return. In the latter version we've specified D as the type argument to use in A forcing B<F>.SetCSubclass to only accept instances of F.

This provides a further degree of type safety that a developer using this type of pattern may be expecting.

Upvotes: 2

Related Questions