Неможливо неявно перетворити тип 'Int' у 'T'


90

Я можу зателефонувати Get<int>(Stat);абоGet<string>(Name);

Але при компіляції я отримую:

Неможливо неявно перетворити тип 'int' у 'T'

і те саме для string.

public T Get<T>(Stats type) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return t;
    }
}

6
Ви, напевно, думаєте, що якщо if перевірив, що T є int, тож у межах блоку ви знаєте, що T є int, і ви повинні мати змогу неявно перетворити int на T. Але компілятор не призначений слідувати цим міркуванням, він просто знає що загалом T не походить від int, тож не допускає неявного перетворення. (І якби компілятор підтримав це, верифікатор цього не зробив би, тож скомпільована збірка не підлягала б
перевірці

Відповіді:


132

Кожного разу, коли ви виявляєте, що вмикаєте тип у загальному, ви майже напевно робите щось не так . Дженерики повинні бути загальними ; вони повинні функціонувати однаково повністю незалежно від типу .

Якщо T може бути лише int або string, спочатку взагалі не пишіть свій код таким чином. Напишіть два методи: один, який повертає int, а другий - рядок.


1
Отримайте <Car>, де автомобільні знаряддя Iconvertible спричинять поломку. Коли хтось побачить, що у вас є загальний метод, він припустить, що може передати все, що реалізує IConvertible.
Tjaart

10
Я можу лише частково погодитися з вами, @Eric. У мене ситуація, коли мені доводиться аналізувати масиви, що зберігаються в XML-тегах. Проблема полягає в тому, що специфікація, якій слід документ XML (у моєму випадку - COLLADA), говорить, що такі масиви можуть бути не тільки float, int та bool, але й деякі користувацькі типи. Однак, якщо ви отримаєте float [] (теги масивів містять тип збережених даних у своїх іменах: float_array зберігає floats), вам потрібно проаналізувати рядок як масив floats, що вимагає використання деякого IFormatProvider). Я, очевидно, не можу використовувати "T.Parse (...)". Тому для невеликого підмножини випадків мені потрібно використовувати таке перемикання.
rbaleksandar

1
Ця відповідь утримає вас від кролячої нори. Я хотів зробити загальну функцію для int, int?, bool, bool?, string, і, здавалося б, це було неможливо.
Джесс

Це робить перехід на загальний перелічений тип практичним.
Девід А. Грей,

1
Я не хотів використовувати це як відповідь. Але він правий. Я хотів перевірити тип і, якщо конкретний, встановити для нього властивість. Рішенням було створити метод, який брав би строго набраний параметр.
Matt Dawdy

139

Ви повинні мати можливість просто використовувати Convert.ChangeType()замість власного коду:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

20
Як щодоreturn (T)(object)PlayerStats[type];
maxp

11
public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return (T)t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return (T)t;
    }
}

2
return (T) t;тому що ніякі нульові перевірки не потрібні.
BoltClock

Це вище не буде скомпільовано для мене. T повинен бути посилальним типом для компіляції "як".
Роберт Шмідт

9

ChangeTypeце, мабуть, ваш найкращий варіант. Моє рішення схоже на рішення, яке надає BrokenGlass, з невеликою кількістю логіки спроб ловити.

static void Main(string[] args)
{
    object number = "1";
    bool hasConverted;
    var convertedValue = DoConvert<int>(number, out hasConverted);

    Console.WriteLine(hasConverted);
    Console.WriteLine(convertedValue);
}

public static TConvertType DoConvert<TConvertType>(object convertValue, out bool hasConverted)
{
    hasConverted = false;
    var converted = default(TConvertType);
    try
    {
        converted = (TConvertType) 
            Convert.ChangeType(convertValue, typeof(TConvertType));
        hasConverted = true;
    }
    catch (InvalidCastException)
    {
    }
    catch (ArgumentNullException)
    {
    }
    catch (FormatException)
    {
    }
    catch (OverflowException)
    {
    }

    return converted;
}

Моїм випадком використання є конкретний клас, що походить від загального абстрактного класу. Клас позначений абстрактним, оскільки він визначає абстрактний метод, який діє на загальний приватний член базового класу. Загальний використовує обмеження C # 7.3 Enum для свого загального типу. Я щойно успішно пройшов тест, і він працює точно так, як я сподівався.
Девід А. Грей,

8

Спробуйте це:

public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        return (T)(object)Convert.ToInt16(PlayerStats[type]);

    }
    if (typeof(T) == typeof(string))
    {

        return (T)(object)PlayerStats[type];
    }
}

Дякую, це допомогло, моя потреба інша. Я пишу фіктивний метод для існуючого статичного методу, щоб я міг його перевірити. Використовуючи це osherove.com/blog/2012/7/8/…
Есен

8

Насправді ви можете просто перетворити його в, objectа потім у T.

T var = (T)(object)42;

Приклад для bool:

public class Program
{
    public static T Foo<T>()
    {
        if(typeof(T) == typeof(bool)) {
            return (T)(object)true;
        }

        return default(T);
    }

    public static void Main()
    {
        bool boolValue = Foo<bool>(); // == true
        string stringValue = Foo<string>(); // == null
    }
}

Іноді така поведінка бажана. Наприклад, при реалізації або перевизначенні загального методу з базового класу або інтерфейсу, і ви хочете додати деякі різні функціональні можливості на основі Tтипу.


6

Враховуючи, що логіка @BrokenGlass ( Convert.ChangeType) не підтримує тип GUID.

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

Помилка : Недійсний привід із "System.String" на "System.Guid".

Натомість використовуйте логіку нижче за TypeDescriptor.GetConverterдопомогою додавання System.ComponentModelпростору імен.

public T Get<T>(Stats type) where T : IConvertible
{
    (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(PlayerStats[type])
}

Прочитайте це .



0

Ви можете просто кинути, як показано нижче,

public T Get<T>(Stats type) where T : IConvertible
{
  if (typeof(T) == typeof(int))
  {
    int t = Convert.ToInt16(PlayerStats[type]);
    return t as T;
  }
 if (typeof(T) == typeof(string))
 {
    string t = PlayerStats[type].ToString();
    return t as T;
 }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.