0

The following code works fine:

public static void Main()
{
    Foo<int>(5);
}

private static void Foo<T>(T x)
{
    Bar((int)(object)x);
}

private static void Bar(int x)
{
}

However, my Bar method comes from a third-part library (Json.NET) that implements multiple overloads. Example:

private static void Bar(string x)
{
}

If I call Foo<int>(5), the Bar((int)(object)x) conversion works fine, but calling Foo<string>("") breaks at runtime (for obvious reasons, you can't convert string to int).

So, I would like to change the Bar((int)(object)x) conversion to a generic Bar((T)(object)x) conversion, but this gives the following compilation error:

cannot convert 'T' to 'int'

So, is it possible to convert object to T or the only solution is to using a switch-case convertion?

cash
  • 475
  • 2
  • 7
  • 15
  • Yes - a switch/case, reflection, or dynamic. The C# compiler needs to pick the right overload to call at compile-time, but `T` isn't known until runtime. Therefore you can't get the compiler to pick a particular overload at compile-time, based on `T`. However in your case, step back to `Main`, and you know that `T` is going to be an `int`. If you can avoid losing this information, that will help, e.g. by passing the right `Bar` overload as an `Action`, or by not calling *through* a generic method. More details on your situation would help. – canton7 Apr 04 '19 at 13:07
  • If you have a limited set of types probably [this answer](https://stackoverflow.com/a/25400131/3959259) can help. – Bill Tür stands with Ukraine Apr 04 '19 at 13:14
  • @canton7 Actually `T` is known at compile time, not at runtime. The problem is that `T` isn't constrained to only types that the overloaded function can take and compiler knows that and it's the reason why it doesn't allow it to compile. It has to be constrained to only types that the Bar can take, if it is possible. – CrudaLilium Apr 04 '19 at 13:23
  • @ThomasSchremser in your link, Jon Skeet said that `copyAction((T)(object) GetStringValue(field))` works, but I can't see it working in my example. However, the dictionary-delegate solution is not what I would like to implement, but is better than the switch-case approach. – cash Apr 04 '19 at 13:31
  • 1
    @CrudaLilium The method `Foo` has no knowledge that `T` will be an `int`, or anything else. The JIT emits an implementation of `Foo` at the point that it's first needed. The compiler *could* do some static analysis and determine that `Foo` is called at compile-time, but it doesn't (unless you're doing some sort of AOT). – canton7 Apr 04 '19 at 13:32
  • @canton7 I'm sorry I was wrong. You are right, I somehow though that used generic types are already precompiled with the assembly. Guess I got it from backwards thinking from cases where generics are properly constrained and work. – CrudaLilium Apr 04 '19 at 13:48

1 Answers1

0

The error actually means that the compiler doesn't know which overload of Bar to invoke, since T could be any type at compile time.

You would need check if T is int or string at runtime and explicitly cast to the needed type.

if (typeof(T) == typeof(int))
  Bar((int)(object)x);
else if (typeof(T) == typeof(string))
  Bar((string)(object)x);
else
  throw new Exception();
markonius
  • 625
  • 6
  • 25
  • That is absolutely true! That was a brainfart. Edited. Honestly, I was aiming for `switch(x){ case int i:...`, but that wouldn't work either, for the same reason invoking `Bar` doesn't. – markonius Apr 04 '19 at 14:04