Conversão de tipo genérico FROM string

234

Eu tenho uma classe que quero usar para armazenar "propriedades" para outra classe. Essas propriedades simplesmente têm um nome e um valor. Idealmente, o que eu gostaria é poder adicionar propriedades digitadas , para que o "valor" retornado seja sempre do tipo que eu quero que seja.

O tipo deve sempre ser um primitivo. Esta classe subclasses uma classe abstrata que basicamente armazena o nome e o valor como string. A ideia é que essa subclasse adicione um pouco de segurança de tipo à classe base (além de me poupar em algumas conversões).

Então, eu criei uma classe que é (aproximadamente) isso:

public class TypedProperty<DataType> : Property
{
    public DataType TypedValue
    {
        get { // Having problems here! }
        set { base.Value = value.ToString();}
    }
}

Então a questão é:

Existe uma maneira "genérica" ​​de converter de string de volta para um primitivo?

Não consigo encontrar nenhuma interface genérica que vincule a conversão em geral (algo como ITryParsable teria sido o ideal!).

Rob Cooper
fonte
Eu estaria interessado em ver um exemplo de sua classe concreta, mesmo apenas um trecho. :)
Jon Limjap
você pode postar as partes relevantes da sua classe base?
JJS 11/07
Gostaria de saber se alguém pode obter as respostas aqui trabalhando no .Net Standard 1.2: /
Dan Rayson

Respostas:

374

Não tenho certeza se entendi suas intenções corretamente, mas vamos ver se essa ajuda.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
        set { base.Value = value.ToString();}
    }
}
lubos hasko
fonte
Estive pensando por alguns dias como desserializar um fluxo em um tipo genérico. Obrigado :)
Trap
3
Concordo, embora Convert.ChangeTypenão seja uma solução muito universal e extensível, ele funciona para os tipos mais básicos. se for necessário algo melhor, não há problema em agrupar esse método em algo maior, como sugerido por Tim, ou usar um método de conversão diferente.
você precisa saber é o seguinte
18
Eu definitivamente adicionar o onde T: IConvertible
miket
5
O tipo T não deve ser IConvertível, mas o Tipo de base. O valor deve.
chapluck
74

O método de lubos hasko falha para anuláveis. O método abaixo funcionará para anuláveis. Eu não pensei nisso, no entanto. Encontrei-o no Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Crédito para "Tuna Toksoz"

Uso primeiro:

TConverter.ChangeType<T>(StringValue);  

A turma está abaixo.

public static class TConverter
{
    public static T ChangeType<T>(object value)
    {
        return (T)ChangeType(typeof(T), value);
    }

    public static object ChangeType(Type t, object value)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(t);
        return tc.ConvertFrom(value);
    }

    public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
    {

        TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
    }
}
Tim Coker
fonte
eu adicionaria opções de conversão Fallback para enums e outras estruturas complexas, mas boa chamada.
quer
2
Por que o RegisterTypeConverter? Precisamos registrar os conversores antes da mão? (infelizmente o link está morto, então eu não poderia ler sobre ele)
Cohen
Para várias conversões, você provavelmente deve criar tc(a TypeConverter) apenas uma vez. TypeConverteré lento porque usa reflexão para procurar o arquivo TypeConverterAttribute. Se você inicializar um único TypeConvertercampo privado , poderá reutilizar TypeConvertervárias vezes.
Kevin P. Arroz
Funciona bem, mas se T é um object, lança uma exceção. Consegui contornar isso usando `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {objeto o = (objeto) StringValue; voltou para; } `como substituto para a amostra de uso TConverter.ChangeType<T>(StringValue)
Matt
14

Para muitos tipos (número inteiro, duplo, DateTime etc.), existe um método de Análise estático. Você pode invocá-lo usando reflexão:

MethodInfo m = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );

if (m != null)
{
    return m.Invoke(null, new object[] { base.Value });
}
dbkk
fonte
8
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptoré uma classe que possui um método GetConvertorque aceita um Typeobjeto e, em seguida, você pode chamar o ConvertFrommétodo para converter o valueobjeto especificado.

Dinesh Rathee
fonte
Pessoalmente, acho que essa interface é melhor para lidar com a conversão, em vez do método Convert.ChangeType, já que você precisa implementar a interface IConvertible em toda a sua classe.
Sauleil 29/11/19
3

Você poderia usar uma construção como uma classe de características . Dessa forma, você teria uma classe auxiliar parametrizada que sabe como converter uma string em um valor de seu próprio tipo. Então seu getter pode ficar assim:

get { return StringConverter<DataType>.FromString(base.Value); }

Agora, devo salientar que minha experiência com tipos parametrizados é limitada ao C ++ e seus modelos, mas imagino que haja alguma maneira de fazer o mesmo tipo de coisa usando genéricos C #.

Greg Hewgill
fonte
As versões genéricas não existem em C #.
Shimmy Weitzhandler
3

Verifique a estática Nullable.GetUnderlyingType. - Se o tipo subjacente for nulo, o parâmetro do modelo não seráNullable , e podemos usá-lo diretamente. - Se o tipo subjacente não for nulo, use o tipo subjacente na conversão.

Parece funcionar para mim:

public object Get( string _toparse, Type _t )
{
    // Test for Nullable<T> and return the base type instead:
    Type undertype = Nullable.GetUnderlyingType(_t);
    Type basetype = undertype == null ? _t : undertype;
    return Convert.ChangeType(_toparse, basetype);
}

public T Get<T>(string _key)
{
    return (T)Get(_key, typeof(T));
}

public void test()
{
    int x = Get<int>("14");
    int? nx = Get<Nullable<int>>("14");
}
Bob C
fonte
1

Com inspiração na resposta de Bob , essas extensões também suportam conversão de valor nulo e toda conversão primitiva de volta e quarta.

public static class ConversionExtensions
{
        public static object Convert(this object value, Type t)
        {
            Type underlyingType = Nullable.GetUnderlyingType(t);

            if (underlyingType != null && value == null)
            {
                return null;
            }
            Type basetype = underlyingType == null ? t : underlyingType;
            return System.Convert.ChangeType(value, basetype);
        }

        public static T Convert<T>(this object value)
        {
            return (T)value.Convert(typeof(T));
        }
}

Exemplos

            string stringValue = null;
            int? intResult = stringValue.Convert<int?>();

            int? intValue = null;
            var strResult = intValue.Convert<string>();
Ghominejad
fonte
0
public class TypedProperty<T> : Property
{
    public T TypedValue
    {
        get { return (T)(object)base.Value; }
        set { base.Value = value.ToString();}
    }
}

Estou usando a conversão através de um objeto. É um pouco mais simples.

Mastahh
fonte
graças eu tenho que converter um T em uma interface eo objeto de conversão T simples funciona corretamente, rápidas e fáceis graças
LXG
5
(int) (objeto) "54"; é uma FATALIDADE !, isso não é VB!
quer
0

Eu usei a resposta lobos e funciona. Mas tive um problema com a conversão de duplas devido às configurações de cultura. Então eu adicionei

return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);
anhoppe
fonte
0

Mais uma variação. Manipula Anuláveis, bem como situações em que a sequência é nula e T não é anulável.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get
        {
            if (base.Value == null) return default(T);
            var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            return (T)Convert.ChangeType(base.Value, type);
        }
        set { base.Value = value.ToString(); }
    }
}
Todd Menier
fonte
0

Você pode fazer isso em uma linha, como abaixo:

YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));

Feliz codificação;)

Hemendra
fonte
Quando você compartilhou o código como resposta, tente explicá-lo.
Yunus Temurlenk 6/03