Значення типу "T" неможливо перетворити в


146

Ймовірно, це питання початківця, але Google на диво не дав відповіді.

У мене є цей досить штучний метод

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        T newT1 = "some text";
        T newT2 = (string)t;
    }

    return t;
}

Виходячи з C ++ фону, я очікував, що це спрацює. Однак він не може компілювати "Неможливо неявно перетворити тип" T "у рядок" та "Неможливо перетворити тип" T "у рядок" для обох вищезазначених призначень.

Я або роблю щось концептуально неправильне, або просто неправильний синтаксис. Будь ласка, допоможіть мені розібратися з цим.

Дякую!


20
IMO, якщо ви перевіряєте типи у своєму генеричному коді, то, можливо, генеричні файли не є правильним рішенням вашої проблеми.
Остін Салонен

Вираз typeof(T) == typeof(string)вирішується під час виконання, а не під час компіляції. Таким чином, наступний рядок у блоці є недійсним.
Стів Гуїді

8
(T) Convert.ChangeType (newT1, typeof (T))
vespiha

2
@vsapiha, працює лише в тому випадку, якщо об'єкт реалізує IConvertible. Солодкість, якщо це все-таки є.
ouflak

Відповіді:


285

Хоча це всередині ifблоку, компілятор не знає, що Tце string.
Тому це не дозволяє вам зняти участь. (З тієї ж причини , що ви не можете кинути DateTimeв string)

Вам потрібно подати на object(до якого Tможе бути передано будь- який), а звідти на string(оскільки objectможна подати string).
Наприклад:

T newT1 = (T)(object)"some text";
string newT2 = (string)(object)t;

2
Це працює! Я здогадуюсь, що другий подібний також повинен бути T newT2 = (T) (об'єкт) t; хоча це не оп.
Олексій

2
Додавання: Шаблони C ++, по суті, вирізають і вставляють під час компіляції із заміненими правильними значеннями. У C # фактичний загальний шаблон (а не його «примірник») існує після компіляції, і, таким чином, повинен (вибачте за каламбур) загальним за вказаними межами типу.

(рядок) (об’єкт) t; тут нічого не робиться, може також залишити це (рядок) (об'єкт), який є
Доггетт

6
Чому б просто не подати роль "як рядок"? Наприклад, це добре компілюється (я буквально просто склав це без помилок), коли userDefinedValue має тип T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

1
Це відчуває помилку дизайнерів-компіляторів. Якщо всі T можуть бути explcitly приведені до об'єкта, і всі об'єкти можуть бути explcitly передані в рядок, тоді повинно існувати перехідне правило, яке T може бути явно передано в рядок. Якщо ви неправильно виконуєте кастинг, то може статися помилка запуску.
P.Brian.Mackey

10

Обидві лінії мають однакову проблему

T newT1 = "some text";
T newT2 = (string)t;

Компілятор не знає, що T - це рядок і тому не може знати, як це призначити. Але оскільки ви перевірили, ви можете просто змусити це

T newT1 = "some text" as T;
T newT2 = t; 

вам не потрібно вводити t, оскільки це вже рядок, також потрібно додати обмеження

where T : class

2
Неправильно. Це не складеться. Дивіться мою відповідь.
Слакс

2
Компіляції просто чудово (з тим, де це є, додав, що через кілька секунд після публікації я, можливо, пропустив це). На жаль, забув змінити акторський склад
Доггетт

2

Я знаю подібний код, який ОП розмістив у цьому питанні від загальних аналізаторів. З точки зору продуктивності, ви повинні використовувати його Unsafe.As<TFrom, TResult>(ref TFrom source), яке можна знайти в пакеті System.Runtime.CompilerServices.Unsafe NuGet. Це дозволяє уникнути боксу для типів значень у цих сценаріях. Я також вважаю, що це Unsafe.Asпризводить до меншої кількості машинного коду, що виробляється JIT, ніж двічі лиття (використання (TResult) (object) actualString), але я цього не перевіряв.

public TResult ParseSomething<TResult>(ParseContext context)
{
    if (typeof(TResult) == typeof(string))
    {
        var token = context.ParseNextToken();
        string parsedString = token.ParseToDotnetString();
        return Unsafe.As<string, TResult>(ref parsedString);
    }
    else if (typeof(TResult) == typeof(int))
    {
        var token = context.ParseNextToken();
        int parsedInt32 = token.ParseToDotnetInt32();
        // This will not box which might be critical to performance
        return Unsafe.As<int, TResult>(ref parsedInt32); 
    }
    // other cases omitted for brevity's sake
}

Unsafe.As буде замінено JIT на ефективні інструкції машинного коду, як ви бачите в офіційному репортажі CoreFX:

Вихідний код небезпечних


1

Якщо ви перевіряєте наявність явних типів, чому ви декларуєте ці змінні як T's?

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        var newT1 = "some text";
        var newT2 = t;  //this builds but I'm not sure what it does under the hood.
        var newT3 = t.ToString();  //for sure the string you want.
    }

    return t;
}

6
Другий рядок створює змінну типу T.
Слакс

Ви запитуєте, чому перевіряти тип? Припустимо, у вас є тип базового поля, яке зберігає objectзначення, із похідними типами, які зберігають stringзначення. Припустимо, ці поля також мають значення "DefaultIfNotProvided", тому вам потрібно перевірити, чи є еквівалентним надане користувачем значення (яке може бути об'єктом чи рядком або навіть числовим примітивом) default(T). Рядок може трактуватися як особливий випадок, коли порожній рядок / пробіл обробляється таким же, як і за замовчуванням (T), тому ви можете перевірити, чи є T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);.
Трайко

0

Ви також отримаєте цю помилку, якщо у вас буде загальна декларація як для вашого класу, так і для вашого методу. Наприклад, наведений нижче код дає цю помилку компіляції.

public class Foo <T> {

    T var;

    public <T> void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

Цей код компілюється (примітка T видалена з декларації методу):

public class Foo <T> {

    T var;

    public void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

-5

Змініть цей рядок:

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

Для цього рядка:

if (t.GetType() == typeof(string))

1
вони однакові
bigworld12

Обидва однакові ... просто за допомогою мовного ключового слова порівняно з використанням API бібліотеки класів.
Абдулхамед
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.