Type restrictions in the interface apply to the base class

I have a base class that defines a generic method like this:

public class BaseClass { public T DoSomething<T> () { ... } } 

Since this class is third-party and does not come with an interface, I define an interface that defines the really necessary methods from this class. Thus, I lose touch and can actually exchange this third-party class for something else. In this example, consider the following interface:

 public interface ISomething { T DoSomething<T> () where T : Foo; } 

As you can see, it defines the same method, but also applies a type constraint to the type parameter, which comes from some other requirements that are not related to this.

Next, I define a subtype of BaseClass that also implements ISomething . This class will be used as a normal implementation of the interface - while the interface will be what the rest of the application will access.

 public class Something : BaseClass, ISomething { // ... } 

Since DoSomething in BaseClass already supports any type parameter T , it should especially support a type parameter that is a subtype of Foo . Therefore, one would expect that the BaseClass subtype already implements the interface. However, I get the following error:

The restrictions for the type parameter T of the BaseClass.DoSomething () method must correspond to the restrictions for the type T parameter of the ISomething.DoSomething () interface method. Instead, consider using an explicit interface implementation.

Now I have two options; the first one is to do what the error suggests and implement the interface explicitly. Secondly, hide the base implementation with new :

 // Explicit implementation T ISomething.DoSomething<T> () { return base.DoSomething<T>(); } // Method hiding public new T DoSomething<T>() where T : Foo { return base.DoSomething<T>(); } 

Both work, although Id probably prefers the second solution to maintain access to this method from the class itself. However, he still leaves the following question:

Why do I need to reimplement the method when the base type already implements it with a less strict restriction (read: none)? Why is this method necessary to implement exactly as it is?

edit: To give the method a little more sense, I changed the return type from void to T In my actual application, I have both general arguments and return values.

+6
source share
3 answers

Of course, this code could be compiled and executed safely:

When an instance of Something is introduced as Something or as BaseClass , the compiler resolves any type to T , and when the same instance is typed as ISomething , it only allows types that inherit Foo . In both cases, you get static verification and safety during operation, as usual.

In fact, the above scenario is exactly what happens when you explicitly implement ISomething . . So, let's see what arguments we can make against the current state of affairs.

For:

  • the proposed solution will not apply to all cases; it depends on the exact method signatures (an argument of type covariant? contravariant? invariant?)
  • it does not require modification of the specification with new text indicating how such cases are handled.
  • he makes a self-declaration of the code - you do not need to study the specified text; current rules regarding explicit implementation of an interface are sufficient
  • it does not impose costs on the development of the C # compiler team (documentation, function implementation, testing, etc.).

Vs:

  • you need to enter more

Given the above and, in addition, the fact that this is not an everyday scenario, IMHO, the conclusion that must be reached is clear: it can be nice to have, but it certainly does not guarantee that you are going to implement it.

+1
source

Try using composition instead of inheritance to implement Something :

 public class Something : ISomething { private readonly BaseClass inner = ...; void DoSomething<T>() where T : Foo { inner.DoSomething<T>(); } } 
+3
source

You can get what you want using the code below. By including a type parameter in the defenition interface, you can make it covariant, which seems to suit the compiler. The Base class remains untouched, and you can obscure the Base implementation and implement the interface with a single method.

 class Program { static void Main() { var something = new Something<Foo>(); var baseClass = (BaseClass)something; var isomething = (ISomething<Foo>)something; var baseResult = baseClass.DoSomething<Bar>(); var interfaceResult = isomething.DoSomething<Bar>(); var result = something.DoSomething<Bar>(); } } class Foo { } class Bar : Foo { } class BaseClass { public T DoSomething<T>() { return default(T); } } interface ISomething<out T> where T : Foo { T DoSomething<T>(); } class Something<T> : BaseClass, ISomething<T> where T : Foo { public new T DoSomething<T>() { return default(T); } } 

Or if you really do not want to specify Foo in the instance

 class Program { static void Main() { var something = new Something(); var baseClass = (BaseClass)something; var isomething = (ISomething)something; var baseResult = baseClass.DoSomething<Bar>(); var interfaceResult = isomething.DoSomething<Bar>(); var result = something.DoSomething<Bar>(); } } class Foo { } class Bar : Foo { } class BaseClass { public T DoSomething<T>() { return default(T); } } interface ISomething { T DoSomething<T>; } interface ISomething<S> : ISomething where S : Foo { new R DoSomething<R>() where R : Foo; } class Something : BaseClass, ISomething { public new T DoSomething<T>() { return default(T); } } 
+1
source

All Articles