Home > Mobile >  In C#, how can I select the correct overload based on a generic argument?
In C#, how can I select the correct overload based on a generic argument?

Time:12-10

// overloads
void f(int x){}
void f(float x){}

// generic
void s<T>(T t){
  f(t); // <<< cannot convert from 'T' to 'int'
}

// use
s(10);

The C# compiler responds that, in the body of s<T>, I cannot convert from 'T' to 'int'. Is there another way to bridge the generic -> overload gap?

(I'm on C# 10)

CodePudding user response:

The key driver for generics is doing basically the same thing with all T; calling different methods per T is the opposite of that; there are ways to do it, but that isn't necessarily optimal. With C# 11, you may be able to use generic math support if your f operations are mathematical. Other than that, it comes down to type testing.

Oddly, this isn't as inefficient as it looks:

void s<T>(T t){
  if (typeof(T) == typeof(int)) {
    f(Unsafe.As<T, int>(ref t));
  }
  else if (typeof(T) == typeof(float)) {
    f(Unsafe.As<T, float>(ref t));
  }
  else {
    // something
  }
}

because the JIT (on modern runtimes) is per-T for all value-type T, and will erase unreachable code. Other than that, you're down to dynamic:

f((dynamic)t);

That is inefficient - it involves boxing and reflection.

CodePudding user response:

The answer on your question is "depended". Depended on what you're trying to achieve.

If you need something like "If it's int or double then use this logic else use common logic" then you should do something like

void Foo(int value) { /* int logic */ }
void Foo(double value) { /* double logic */ }
void Foo<T>(T value) { /* common logic */ }

If you need something like "Here is value with generic type. Do some things and then call specific logic if it's int or double" then you should do something like

void Foo(int value) {}
void Foo(double value) {}
void SomeFunc<T>(T value) 
{
    switch (value)
    {
        case int intValue:
            Foo(intValue);
            break;
        case double doubleValue:
            Foo(doubleValue);
            break;
    }
}

But you should remember that the second code sample will box and unbox argument value. Marc Gravell suggested more efficient way to cast, but this is harder to read too. It's your decision what do you need exactly. More readable or more efficient code.

  • Related