Não é possível converter implicitamente o tipo 'Int' em 'T'

92

Eu posso ligar Get<int>(Stat);ouGet<string>(Name);

Mas, ao compilar, recebo:

Não é possível converter implicitamente o tipo 'int' em 'T'

e a mesma coisa para 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;
    }
}
David W
fonte
6
Você provavelmente está pensando que o bloco if verificou que T é int, portanto, dentro do bloco, você sabe que T é int e deve ser capaz de converter int implicitamente em T. Mas o compilador não foi projetado para seguir esse raciocínio, ele apenas sabe que geralmente T não deriva de int, então não permite a conversão implícita. (E se o compilador o suportasse, o verificador não, então o assembly compilado não seria verificável.)
JGWeissman

Respostas:

132

Sempre que você liga um tipo em um genérico, é quase certo que está fazendo algo errado . Os genéricos devem ser genéricos ; eles devem operar de forma idêntica e completamente independente do tipo .

Se T só puder ser int ou string, não escreva seu código dessa maneira em primeiro lugar. Escreva dois métodos, um que retorna um int e outro que retorna uma string.

Eric Lippert
fonte
1
Pegue <Car> onde o carro implementa IConvertible irá causar quebras. Quando alguém vê que você tem um método genérico, presumirá que pode transmitir qualquer coisa que implemente IConvertible.
Tjaart
11
Posso apenas concordar parcialmente com você, @Eric. Eu tenho uma situação em que tenho que analisar arrays armazenados em tags XML. O problema é que a especificação que o documento XML segue (COLLADA no meu caso) diz que tais arrays podem ser não apenas float, int e bool, mas também alguns tipos personalizados. No entanto, no caso de você obter um float [] (tags de array contêm o tipo de dados armazenados em seus nomes: float_array armazena floats), você precisa analisar a string como um array de floats, que requer o uso de algum IFormatProvider). Obviamente, não posso usar "T.Parse (...)". Portanto, para um pequeno subconjunto de casos, preciso usar essa alternância.
rbaleksandar
1
Essa resposta o manterá fora da toca do coelho. Eu queria fazer uma função genérica para int, int?, bool, bool?, string, e parecia impossível.
Jess,
Isso torna prática uma opção em um tipo enumerado genérico.
David A. Gray de
1
Eu não queria usar isso como resposta. Mas ele está certo. Eu queria verificar o tipo e, se fosse específico, definir uma propriedade nele. A solução foi fazer um método que usasse um parâmetro fortemente tipado.
Matt Dawdy
143

Você deve ser capaz de usar apenas em Convert.ChangeType()vez de seu código personalizado:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}
Vidro quebrado
fonte
22
Que talreturn (T)(object)PlayerStats[type];
maxp
@BrokenGlass Obrigado Está funcionando bem.
ASLIM
@maxp becareful Não funciona para fins gerais, por exemplo, a conversão de tipo não gerenciado requer 'Convert.ChangeType ()'.
ASLIM
10
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;
    }
}
Reza ArabQaeni
fonte
2
return (T) t;porque nenhuma verificação nula é necessária.
BoltClock
Isso acima não compilará para mim. T precisa ser um tipo de referência para "como" para compilar.
Robert Schmidt,
9

ChangeTypeé provavelmente a sua melhor opção. Minha solução é semelhante à fornecida por BrokenGlass com um pouco de lógica try catch.

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;
}
Michael Ciba
fonte
Meu caso de uso é uma classe concreta derivada de uma classe abstrata genérica. A classe é marcada como abstrata porque define um método abstrato que opera no membro privado genérico da classe base. O genérico usa a restrição Enum do C # 7.3 em seu tipo genérico. Acabei de concluir um teste com sucesso, e ele funciona exatamente como eu esperava.
David A. Gray de
8

Experimente isto:

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];
    }
}
Michael Kalinovich
fonte
Obrigado esta ajudado, minha necessidade é diferente. Estou escrevendo um método de simulação para um método estático existente para que possa testá-lo. Usando este osherove.com/blog/2012/7/8/…
Esen
8

Na verdade, você pode simplesmente convertê-lo para objecte depois para T.

T var = (T)(object)42;

Um exemplo para 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
    }
}

Às vezes, esse comportamento é desejável. Por exemplo, ao implementar ou sobrescrever um método genérico de uma classe ou interface base e você deseja adicionar algumas funcionalidades diferentes com base no Ttipo.

GregorMohorko
fonte
6

Considerando que a lógica @BrokenGlass ( Convert.ChangeType) não oferece suporte para o tipo de GUID.

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

Erro : conversão inválida de 'System.String' para 'System.Guid'.

Em vez disso, use a lógica abaixo TypeDescriptor.GetConverteradicionando System.ComponentModelnamespace.

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

Leia isso .

Prasad Kanaparthi
fonte
3

Parece que você precisa de um TypeConverter, consulte esta entrada do blog .

Ian Mercer
fonte
0

Você pode simplesmente lançar como abaixo,

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;
 }
}
Vijayanath Viswanathan
fonte