Changing the type of a parameter when implementing an abstract method

Is there a way to define an abstract type as a parameter of an abstract method, and when this method is implemented in a derived class, do you change the type of the method to accept the derived type?

the code:

public abstract class ProductBase { } public class SomeProduct : ProductBase { } public abstract class A { protected abstract void addProduct(ProductBase p); } // This works public class B : A { protected override void addProduct(ProductBase p) { // Do some work } } // This is what I'd like to do public class C : A { // Compiler error here because I have accepted a SomeProduct and not a ProductBase protected override void addProduct(SomeProduct p) { // Do some work on the specialisation - I can use the SomeProduct directly } } 

It makes some sense in my head. An abstract class indicates that there is a method that derived classes must implement, but they can change the type of the object passed as a parameter, provided that it belongs to the same inheritance chain ...

What I ended up with is removing the AddProduct abstract method from the abstract class, and instead just implement it in the derived class, but then there is no contract for other classes in the future that they need to create their own implementation of AddProduct . And that does not seem right.

Hope this makes sense. Sorry if this is a duplicate question, but I could not find anything by doing a search.

Thanks,
bgs264

+4
inheritance c # abstract-class
source share
3 answers

You can make your class A universal and use a common argument in the addProduct method:

 public abstract class A<TProduct> where TProduct : ProductBase { protected abstract void addProduct(TProduct p); } public class B : A<ProductBase> { protected override void addProduct(ProductBase p) { } } public class C : A<SomeProduct> { protected override void addProduct(SomeProduct p) { } } 
+6
source share

No, this is not possible: suppose you call addProduct for a variable of class A that points to an instance of class C , passing in an object that is ProductBase , but not a SomeProduct . A past instance cannot be converted to SomeProduct , so it will not be compiled in the first place.

Closest to this is a solution with generics:

 public abstract class A<T> where T : ProductBase { protected abstract void addProduct(T p); } 

Then you can define your class C as follows:

 public class C : A<SomeProduct> { protected override void addProduct(SomeProduct p); } 

Of course, this means that you need to enter any type A variables in SomeProduct , as in A<SomeProduct> = new C(); .

You cannot simply have a variable of type A (which provides the addProduct method) without specifying the actual value of an argument of type T On the other hand, calling the addProduct method addProduct not be possible in your solution, as described above.

Now you can enter a non-strongly typed method:

 public abstract class A<T> where T : ProductBase { protected abstract void addProduct(T p); protected void addProductUntyped(ProductBase p) { T typedProduct = p as ProductBase; if (typedProduct != null) { addProduct(typedProduct); } else { throw new ArgumentException("p has an incompatible type."); } } } 

However, such a solution is usually really needed only when there are some users of the class who can know the general type, while others do not.

+1
source share

Honestly, for the implementation code, I will just live with the cast. The only time I will make it β€œbeautiful” is to affect public subscribers:

 protected override void addProduct(ProductBase p) { SomeProduct prod = (SomeProduct)p; // ... } 

One option that I'm not , like myself, is generics:

 public abstract class A<T> where T : ProductBase { protected abstract void addProduct(T p); } public class C : A<SomeProduct> { protected override void addProduct(SomeProduct p) { // ... } } 

The reason I don't like it is because you can no longer use non-generic A , so you lose a lot of abstraction. Another option is a new method for re-declaring parameters. Unfortunately, you cannot new and override at the same level, but the workaround is to use 2 methods:

 public abstract class A { protected void addProduct(ProductBase p) { addProductImpl(p);} protected abstract void addProductImpl(ProductBase p); } public class C : A { new protected void addProduct(SomeProduct p) { addProductImpl(p);} protected override void addProductImpl(ProductBase p) { SomeProduct prod = (SomeProduct) p; // ... } } 
+1
source share

All Articles