Conversión de tipo genérico de cadena

234

Tengo una clase que quiero usar para almacenar "propiedades" para otra clase. Estas propiedades simplemente tienen un nombre y un valor. Idealmente, lo que me gustaría es poder agregar propiedades escritas , de modo que el "valor" devuelto sea siempre del tipo que quiero que sea.

El tipo siempre debe ser primitivo. Esta clase subclasifica una clase abstracta que básicamente almacena el nombre y el valor como una cadena. La idea es que esta subclase agregará algo de seguridad de tipos a la clase base (además de ahorrarme en alguna conversión).

Entonces, he creado una clase que es (aproximadamente) esto:

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

Entonces la pregunta es:

¿Hay una forma "genérica" ​​de convertir de cadena a primitiva?

Parece que no puedo encontrar ninguna interfaz genérica que vincule la conversión en todos los ámbitos (¡algo como ITryParsable hubiera sido ideal!).

Rob Cooper
fuente
Me interesaría ver un ejemplo de tu clase concreta, incluso solo un fragmento. :)
Jon Limjap
¿Puedes publicar las partes relevantes de tu clase base?
JJS
Me pregunto si alguien puede obtener las respuestas aquí trabajando en .Net Standard 1.2: /
Dan Rayson el

Respuestas:

374

No estoy seguro de haber entendido sus intenciones correctamente, pero veamos si esto ayuda.

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
fuente
Me he estado preguntando durante algunos días cómo deserializar una secuencia en un tipo genérico. Gracias :)
Trampa
3
Estoy de acuerdo, aunque Convert.ChangeTypeno es una solución muy universal y extensible, funciona para la mayoría de los tipos básicos. Si se necesita algo mejor, no hay problema en envolver este método en algo más grande como lo sugirió Tim o usar un método de conversión completamente diferente.
lubos hasko
18
Definitivamente agregaría el Where T: IConvertible
MikeT
55
El tipo T no debe ser IConvertible, sino el tipo de base.
chapluck
74

El método de lubos hasko falla para los valores nulables. El siguiente método funcionará para nulables. Sin embargo, no se me ocurrió. Lo encontré a través de Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Crédito para "Tuna Toksoz"

Uso primero:

TConverter.ChangeType<T>(StringValue);  

La clase está abajo.

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
fuente
Agregaría una opción de conversión de Fallback para Enums y otras estructuras complejas, pero es una buena opción.
Tomer W
2
¿Por qué el RegisterTypeConverter? ¿Necesitamos registrar los convertidores de antemano? (desafortunadamente el enlace está muerto, así que no pude leerlo)
Cohen
Para conversiones múltiples, probablemente debería crear tc(la TypeConverter) solo una vez. TypeConverteres lento porque usa la reflexión para buscar el TypeConverterAttribute. Si inicializa un único TypeConvertercampo privado , debería poder reutilizarlo TypeConvertermuchas veces.
Kevin P. Rice el
Funciona bien, pero si T es un object, arroja una excepción. Pude solucionarlo usando `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {objeto o = (objeto) StringValue; volver a; } `como reemplazo para la muestra de uso TConverter.ChangeType<T>(StringValue)
Matt
14

Para muchos tipos (entero, doble, DateTime, etc.), existe un método Parse estático. Puedes invocarlo usando la reflexión:

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

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

TypeDescriptores una clase que tiene un método GetConvertorque acepta un Typeobjeto y luego puede llamar al ConvertFrommétodo para convertir el valueobjeto especificado.

Dinesh Rathee
fuente
Personalmente, creo que esta interfaz es mejor para manejar la conversión en lugar del método Convert.ChangeType, ya que necesita implementar la interfaz IConvertible en toda su clase.
Sauleil
3

Posiblemente podría usar una construcción como una clase de rasgos . De esta manera, tendría una clase auxiliar parametrizada que sabe cómo convertir una cadena a un valor de su propio tipo. Entonces su captador podría verse así:

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

Ahora, debo señalar que mi experiencia con los tipos parametrizados se limita a C ++ y sus plantillas, pero imagino que hay alguna forma de hacer lo mismo con los genéricos de C #.

Greg Hewgill
fuente
Las versiones genéricas no existen en C #.
Shimmy Weitzhandler
3

Verifica la estática Nullable.GetUnderlyingType. - Si el tipo subyacente es nulo, entonces el parámetro de plantilla no esNullable , y podemos usar ese tipo directamente - Si el tipo subyacente no es nulo, entonces use el tipo subyacente en la conversión.

Parece funcionar para mi:

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
fuente
1

Con la inspiración de la respuesta de Bob , estas extensiones también admiten la conversión de valores nulos y todas las conversiones primitivas de vuelta y cuarta.

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

Ejemplos

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

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

Estoy usando la conversión a través de un objeto. Es un poco más simple.

Mastahh
fuente
gracias Tengo que convertir una T en una interfaz y el objeto T de conversión simple funciona correctamente, fácil y rápido gracias
LXG
55
(int) (objeto) "54"; es una FATALIDAD !, esto no es VB!
Tomer W
0

Utilicé la respuesta de lobos y funciona. Pero tuve un problema con la conversión de dobles debido a la configuración de la cultura. Entonces agregué

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

Otra variación más. Maneja Nulables, así como situaciones donde la cadena es nula y T no es anulable.

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
fuente
0

Puede hacerlo en una línea de la siguiente manera:

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

Codificación feliz;)

Hemendra
fuente
Cuando compartió el código como respuesta, intente explicarlo.
Yunus Temurlenk