Значение типа 'T' не может быть преобразовано в

Это, скорее всего, вопрос новичка, но на удивление Google не дал ответа.

У меня есть этот довольно искусственный метод

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

        return t;
    }

Исходя из опыта C++, я ожидал, что это сработает. Однако он не может быть скомпилирован с "Невозможно неявно преобразовать тип" T "в строку" и "Невозможно преобразовать тип" T "в строку" для обоих приведенных выше назначений.

Я либо делаю что-то концептуально неправильно, либо просто у меня неправильный синтаксис. Пожалуйста, помогите мне разобраться с этим.

Спасибо!

5 ответов

Решение

Хотя это внутри if блок, компилятор не знает, что T является string,
Следовательно, он не позволяет вам разыгрывать. (По той же причине, по которой вы не можете разыграть DateTime в string)

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

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

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

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

Компилятор не знает, что T является строкой, и поэтому не может знать, как его назначить. Но так как вы проверили, вы можете просто заставить его

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

вам не нужно приводить t, так как это уже строка, также нужно добавить ограничение

where T : class

Я знаю аналогичный код, который OP опубликовал в этом вопросе из общих парсеров. С точки зрения производительности вам следует использоватьUnsafe.As<TFrom, TResult>(ref TFrom source), который можно найти в пакете NuGet https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe. Это позволяет избежать упаковки для типов значений в этих сценариях. я тоже так думаю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:

Если вы проверяете явные типы, почему вы объявляете эти переменные как 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;
}

Вы также получите эту ошибку, если у вас есть общее объявление для вашего класса и вашего метода. Например, приведенный ниже код выдает эту ошибку компиляции.

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();
    }

}

Измените эту строку:

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

Для этой строки:

if (t.GetType() == typeof(string))
Другие вопросы по тегам