0

Say I have an interface WorksWithType<T> and class MyClass that implements both WorksWithType<TypeA> and WorksWithType<TypeB>.

If my interface looks like

public interface WorksWithType<T> {
     void DoSomething(T foo);
     void DoSomethingElse();
}

it is easy to implement two different DoSomething method overloads in MyClass.

public class MyClass : WorksWithType<TypeA>, WorksWithType<TypeB> {
{
    public void DoSomething(TypeA fooA) { ... } 
    public void DoSomething(TypeB fooB) { ... } 
    ...
}

However, there doesn't seem to be a way to implement overloads of DoSomethingElse. In my mind I feel as though I should be able to change the signature on the interface to be

void DoSomethingElse<T>();

and then overload the class with

public void DoSomethingElse<TypeA>() { ... } 
public void DoSomethingElse<TypeB>() { ... }  

What is the correct approach here, if there is one?

Dan Brenner
  • 880
  • 10
  • 23
  • In order to achieve this you´ll need the generic parameter on the method, not on the interface. – MakePeaceGreatAgain Apr 12 '18 at 19:55
  • @dlatikay: I think they're definitely very closely related, but this one is a better one as it deals with *one* aspect (just the implementation) rather than 1) Why am I getting a warning about type parameter hiding? 2) How do I call the method I want? 3) (Not even clearly stated, partly due to issue 1) How do I implement the interface? – Jon Skeet Apr 12 '18 at 20:02
  • Please, next type you declare an interface start the name with a capital `I`, as in `IWorksWithType`. It is a universal standard in C#. – John Alexiou Apr 13 '18 at 12:27

2 Answers2

3

Assuming you want the two implementations of DoSomethingElse to be different , you need to use explicit interface implementation to distinguish between the calls:

public class TypeA {}
public class TypeB {}

public interface IWorksWithType<T>
{
     void DoSomething(T foo);
     void DoSomethingElse();
}

public class MyClass : IWorksWithType<TypeA>, IWorksWithType<TypeB>
{
    public void DoSomething(TypeA fooA) {}
    public void DoSomething(TypeB fooB) {} 

    // Note the syntax here - this indicates which interface
    // method you're implementing        
    void IWorksWithType<TypeA>.DoSomethingElse() {}
    void IWorksWithType<TypeB>.DoSomethingElse() {}
}

You don't have to make both of them use explicit interface implementation. For example:

public class MyClass : IWorksWithType<TypeA>, IWorksWithType<TypeB>
{
    public void DoSomething(TypeA fooA) {}
    public void DoSomething(TypeB fooB) {} 

    // Explicit interface implementation
    void IWorksWithType<TypeA>.DoSomethingElse() {}
    // Implicit interface implementation
    public void DoSomethingElse() {}
}

If you don't need the implementations to be different, you can just have three methods of course:

public class MyClass : IWorksWithType<TypeA>, IWorksWithType<TypeB>
{
    public void DoSomething(TypeA fooA) {}
    public void DoSomething(TypeB fooB) {}

    // Implementation of both IWorksWithType<TypeA>.DoSomethingElse()
    // and IWorksWithType<TypeB>.DoSomethingElse()
    public void DoSomethingElse() {}
}

That's assuming you to want the type parameter to be on the interface. You could put it on the method instead, but that really represents a very different interface - and you wouldn't be able to say that MyClass can only call DoSomethingElse for types TypeA and TypeB, for example.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

By the current C# specifications (draft 6) https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/introduction

The signature of a method must be unique in the class in which the method is declared. The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. The signature of a method does not include the return type

(emphasis mine)

So, unfortunately,

void MyMethod<TypeA>() 

and

void MyMethod<TypeB>()

would not have different signatures, so you cannot, per the specs, define both of them : they differ only by the type of their type parameter, but not by the number of their type parameter

Other answers have already pointed out how you could work around this (the explicit interface implementation is a good idiomatic option in my opinion)

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • More importantly, `void MyMethod()` would be introducing `TypeA` as a type *parameter*, whereas I believe the OP would want to be effectively using it as a type *argument*. Fundamentally I think they're reaching for syntax to solve a problem, but they've reached for the wrong syntax. – Jon Skeet Apr 12 '18 at 20:03
  • Yes, maybe I misunderstood a bit OP's goal, but hopefully the specs might shed some light to the confusion. – Pac0 Apr 12 '18 at 20:05
  • 1
    My expectation is that the OP is really just looking to implement the two interfaces, and is stuck with the clash. It would be very strange to name a type parameter `TypeA` when one had an actual type called `TypeA` as well. (Can you imagine calling the method using `TypeB` as a type argument for a type parameter called `TypeA`? Confusing as heck.) – Jon Skeet Apr 12 '18 at 20:08
  • Yes, and the explicit interface implementation you showed nicely covers this needs without any crazy headache ! – Pac0 Apr 12 '18 at 20:11