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

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.


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

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

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

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];
    }
}

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.


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 .



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;
 }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.