3

I have an interface with the generic method:

interface IConverter
{
    void Convert<T>(T value);
}

I would like to implement it for any type, but also I know that for string the method logic can be simplified, so I'd like to overload it specifically for string (like in this question):

class Converter : IConverter
{
    public void Convert<T>(T value)
    {
        Console.WriteLine("Generic Method " + value);
    }

    public void Convert(string value)
    {
        Console.WriteLine("String Method " + value);
    }
}

That works fine when I have instance of Converter and call method directly. The code

var converter = new Converter();
converter.Convert("ABC");
converter.Convert(123);

outputs

String Method ABC
Generic Method 123

However, when I work with interface (like in any app with DI), I can't call my overload for string. The code

var converter = (IConverter)new Converter();
converter.Convert("ABC");
converter.Convert(123);

outputs:

Generic Method ABC
Generic Method 123

Is there anyway to accomplish calling of string overloaded method without type checking like

if (typeof(T) == typeof(string))
    ...

?

Community
  • 1
  • 1
kyrylomyr
  • 12,192
  • 8
  • 52
  • 79

5 Answers5

3

No. You cast converter to IConverter, so there is only one method visible on your converter variable: Convert<T>(T value).

You can overcome this by adding Convert(string) to the interface, or don't cast to the interface at all and keep using your Converter class. Only then the other method will be visible.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Ok. But from my understanding JIT will create string version of method. And such string version is already exists implemented by me... – kyrylomyr Jul 26 '16 at 08:38
  • 1
    Still it uses the method known from the interface. It doesn't magically call another method just because it looks the same. – Patrick Hofman Jul 26 '16 at 08:38
  • 1
    @KyryloM: JITter will take the generic `Convert` method and create a JITtted specialized `Convert` method, like it's supposed to do. Your other method is, as far as C# is concerned, the same as having a method with a completely different name. – vgru Jul 26 '16 at 08:40
  • 1
    Probably you are right. I've checked the full method name from the stack trace. For the generic version its a `Converter.Convert[T](T)`, for string method - `Converter.Convert(System.String)` – kyrylomyr Jul 26 '16 at 09:00
1

There are two ways,

Either you include that method in your interface like

interface IConverter
{
    void Convert<T>(T value);
    void Convert(string value);
}

Have that method specific to string created as extension method

public static void Convert(this string str, string value)
{
  // code here
}
Rahul
  • 76,197
  • 13
  • 71
  • 125
  • I guess you mean `public static void Convert(this IConverter str, ...`? – Jcl Jul 26 '16 at 08:43
  • @Jcl, NO, I mean what I said. It's a extension method for the `string` type – Rahul Jul 26 '16 at 08:45
  • Then it's just not the same... you'd need to do: `stringVariable.Convert(anotherString);`... the OP wants: `iconverterVariable.Convert(someString)` – Jcl Jul 26 '16 at 08:46
  • @Jcl, didn't I mentioned it's an alternative along with including the method itself part of interface definition – Rahul Jul 26 '16 at 08:47
  • again, it's an alternative that doesn't make sense for the question... at most: `public static void Convert(this string str)` without the `string value` parameter. The OP has *one* string variable, you need *two* for your extension (the one you are calling the method on, and the one passed in as a parameter) – Jcl Jul 26 '16 at 08:49
1

Perhaps your design is inverted, and your interface should be generic:

interface IConverter<T>
{
    void Convert(T value);
}

This would allow you to implement specific types in a specific way:

class BaseConverter<T> : IConverter<T>
{
    public void Convert(T value) 
    { /* some common way */ }
}

class StringConverter : IConverter<string>
{
    public void Convert(string value) 
    { /* strings get converted here */ }
}

And you would likely have a "factory" class which would provide the right converter instance:

class ConverterFactory
{
    // this is basically "poor man's" service locator,
    // but the same idea works with DI, where you
    // would register individual comparers 

    public IConverter<T> Create<T>()
    {
         // use a Dictionary, or add that
         // typeof(T) == typeof(string) check here
    }
}

This is similar to how, for example, IComparer<T> is defined in .NET BCL, coupled with Comparer<T>.Default.

This is also the approach that would work better with DI, because your IConverter<T> depends on T, while having a single non-generic IConverter cannot tell DI which specific instance to inject.

vgru
  • 49,838
  • 16
  • 120
  • 201
0

You could do some type-checking within your generic-method.

public void Convert<T>(T value)
{
    Console.WriteLine(typeof(T) == typeof(string) ? "String Method" : "Generic Method " + value);
}

However doing so omits all what is ment to be generic, doesn´t it? Having said this is not recommended.

Anyway only because two methods have the same name does not mean that they are equal, for the compiler Convert(string) and Convert<T>(T) are completely different methods. The interface however knows only the generic one, so when casting to it there is no knowledge on the string-method at all.

As Patrick already mentioned omit the cast or implement the string-method on the interface. Another - also dirty hack - would be to redirect the generic call to the string-method:

class Converter : IConverter
{
    public void Convert<T>(T value)
    {
        if (typeof(T) == typeof(string) this.Convert((string) value);
        else Console.WriteLine("Generic Method " + value);
    }

    public void Convert(string value)
    {
        Console.WriteLine("String Method " + value);
    }
}
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
0

If you really, desperately want to keep the interface the same you can use a type check:

class Converter : IConverter
{
    public void Convert<T>(T value)
    {
        if (value is string)
        {
            Console.WriteLine("String Method " + (string)value);
        }
        else
        {
            Console.WriteLine("Generic Method " + value);
        }
    }
}

Alternatively, as said by others, you can just add Convert(string) to IConverter.

toneo
  • 111
  • 7