Следующий код работает нормально:
public static void Main()
{
Foo<int>(5);
}
private static void Foo<T>(T x)
{
Bar((int)(object)x);
}
private static void Bar(int x)
{
}
Однако мой метод Bar взят из сторонней библиотеки (Json.NET), которая реализует несколько перегрузок. Пример:
private static void Bar(string x)
{
}
Если я вызываю Foo<int>(5), преобразование Bar((int)(object)x) работает нормально, но вызов Foo<string>("") прерывается во время выполнения (по понятным причинам вы не можете преобразовать строку в int).
Итак, я хотел бы изменить преобразование Bar((int)(object)x) на общее преобразование Bar((T)(object)x), но это дает следующую ошибку компиляции:
cannot convert 'T' to 'int'
Итак, возможно ли преобразовать объект в T или единственным решением является использование преобразования с переключением регистра?
Если у вас ограниченный набор типов, вероятно, этот ответ может помочь.
@canton7 На самом деле T известно во время компиляции, а не во время выполнения. Проблема в том, что T не ограничивается только типами, которые может принимать перегруженная функция, и компилятор знает об этом, и это причина, по которой он не позволяет ей компилироваться. Он должен быть ограничен только типами, которые может принимать Bar, если это возможно.
@ThomasSchremser в вашей ссылке Джон Скит сказал, что copyAction((T)(object) GetStringValue(field)) работает, но я не вижу, чтобы это работало в моем примере. Однако решение делегата словаря — это не то, что я хотел бы реализовать, но оно лучше, чем подход с переключателем.
@CrudaLilium Метод Foo<T> не знает, что T будет int или чем-то еще. JIT выдает реализацию Foo<int> в тот момент, когда это необходимо. Компилятор мог выполняет статический анализ и определяет, что Foo<int> вызывается во время компиляции, но это не так (если вы не выполняете какой-то AOT).
@ canton7 Прости, что ошибся. Вы правы, я почему-то думал, что используемые дженерик-типы уже прекомпилированы со сборкой. Думаю, я получил это от обратного мышления из случаев, когда дженерики должным образом ограничены и работают.





На самом деле ошибка означает, что компилятор не знает, какую перегрузку Bar вызывать, поскольку T может быть любого типа во время компиляции.
Вам нужно будет проверить, является ли Tint или string во время выполнения, и явно привести к нужному типу.
if (typeof(T) == typeof(int))
Bar((int)(object)x);
else if (typeof(T) == typeof(string))
Bar((string)(object)x);
else
throw new Exception();
Это абсолютно верно! Это был мозговой удар. Отредактировано. Честно говоря, я стремился к switch(x){ case int i:..., но это тоже не сработало бы, по той же причине, по которой не работает вызов Bar.
Да - переключатель/кейс, отражение или динамический. Компилятор C# должен выбрать правильную перегрузку для вызова во время компиляции, но
Tнеизвестно до времени выполнения. Поэтому вы не можете заставить компилятор выбирать конкретную перегрузку во время компиляции на основеT. Однако в вашем случае вернитесь кMain, и вы знаете, чтоTбудетint. Если вы можете избежать потери этой информации, это поможет, например. передавая правильную перегрузкуBarкакAction<T>или не вызывая через универсальный метод. Более подробная информация о вашей ситуации поможет.