2010-10-02 9 views
14

Dada la clase:extendiéndose Convert.ChangeType para producir tipos definidos por el usuario a petición

public class Foo 
{ 
    public string Name { get; set; } 
} 

¿Es posible tener una instancia de Foo creado a partir de una cadena a través Convert.ChangeType:

Type type = typeof(Foo); 
object value = "one"; 

value = System.Convert.ChangeType(value, type); 

Así es como una API de terceros intenta reconstruir objetos. Alguien mencionó esto es posible con los operadores implícitos, pero desde mi comprensión que me permita hacer lo siguiente, no crea el objeto:

Foo foo = new Foo() { Name = "one" }; 
string fooAsString = foo; // implicit conversion -- no cast needed 

¿Hay una manera de crear el objeto de esta manera? Además, tengo la capacidad de cambiar el Convert.ChangeType si hay otra forma de hacerlo.

Actualización: La razón que pido es porque lanza y la excepción:

fundido no válida del 'System.String' a 'JibbaJabba + Foo'.

y agregar el operador no resolvió el problema.

+0

Debo señalar que probé el operador implícito y no funcionó. – blu

+0

Tenga en cuenta que puede tener un operador implícito en cualquier dirección, pero todavía es una característica del compilador de C# y no va a ser utilizado por código ya compilado. Lo más probable es que tenga que mirar y ver si la biblioteca de terceros le permite crear los objetos usted mismo, ¿tal vez hay una sobrecarga que acepta un delegado 'Converter '? –

Respuesta

19

Según the MSDN documentation:

Para que la conversión tenga éxito, valor debe implementar la interfaz IConvertible , porque el método simplemente envuelve una llamada a un método apropiado IConvertible. El método requiere la conversión de valor a tipo de conversión compatible.

En cuanto a la interfaz IConvertible, que tiene un método ToType. Podrías probar eso, tal vez? (Negación:. Yo no tengo Es sólo una idea.)

Editar: En su caso, parece que desea convertir de un string a un Foo.Dado que el tipo string (obviamente) no define una conversión a Foo en su implementación IConvertible, creo que no tiene suerte.


actualización: No quiero sugerir que esta es la forma en que siempre debe acercarse a este tipo de problema, pero ...

Me tomó un vistazo al código de Convert.ChangeType en Reflector . Es largo; No lo reproduciré aquí. Pero básicamente se está haciendo como dice la documentación: se sólo funciona si:

  • El parámetro value es una instancia no nula de un tipo que implementa IConvertible, o:
  • El tipo del parámetro value y el parámetro conversionType es el mismo (entonces: Convert.ChangeType(myFoo, typeof(Foo)) también funcionaría, aunque sería bastante inútil).

entonces, ciclos a través de todos los tipos soportados por IConvertible (que obviamente no incluye ninguna tipos definidos por el usuario) y en última instancia, utiliza ToType como un repliegue.

Por lo tanto, tenemos que mirar la implementación del tipo string de ToType.

Lamentablemente, es una línea desafortunado:

return Convert.DefaultToType(this, type, provider); 

¿Qué DefaultToType hacer? Exactamente lo mismo que ChangeType (menos la caída de ToType, obviamente para evitar la recursión infinita).

Así que esto simplemente no va a funcionar.

Si está absolutamente vinculado a esta biblioteca de terceros que usa Convert.ChangeType detrás de escena, le recomendaría contactar al desarrollador de la biblioteca y pedirle que extienda su API de alguna manera que le permita lograr lo que está intentando cumplir. Algunas posibilidades podrían ser:

  • La aceptación de un parámetro o Converter<string, T>Func<string, T> delegado opcional, según lo sugerido por Ben Voigt en un comentario.
  • aceptar un parámetro TypeConverter
  • La aceptación de un parámetro de algún tipo que implementa una interfaz como IParser<T>

De todos modos, lo mejor de la suerte.

+0

Veo eso, buena captura en MSDN sobre por qué ocurre la excepción. Parece que tengo una manguera y tendré que encontrar un enfoque alternativo. Voy a +1 y dejar la pregunta abierta por un momento y ver si alguien más tiene una idea. – blu

+0

@blu: Sí, nunca se sabe. Algunas veces los documentos revelan solo parte de la historia. –

0

object value1 = "one";

Foo value2 = (Foo) System.Convert.ChangeType (value, typeof (Foo));

Editar:


Ok entonces, si usted está tratando de crear el tipo de Foo de cadena, se puede utilizar la reflexión:

String controlToCreate = "Foo"; 
Type typeofControl = Type.GetType(controlToCreate,true); 
+0

Eso no funciona – blu

+0

La actualización tampoco funciona. La segunda parte de la respuesta no se relaciona con la pregunta. – blu

+0

El OP específicamente quiere hacer que esto funcione con 'ChangeType' porque está siendo utilizado por una biblioteca de terceros. –

2

El lanzamiento directo desde la cuerda no funcionará como ya señaló Dan Tao. ¿Podrías quizás implementar tu propio contenedor para la cadena y usar eso? Algo así como

class MyString: IConvertible { 

    public string Value { get; set; } 

    ... 
    object IConvertible.ToType(Type conversionType, IFormatProvider provider) { 
    if (conversionType == typeof(Foo)) 
     return new Foo { Name = Value }; 
    throw new InvalidCastException(); 
    } 

} 
... 
MyString myString = new MyString{Value="one"}; 
Foo myFoo = (Foo)Convert.ChangeType(myString, typeof(Foo)); 

No sabe si es una idea útil, pero de todos modos ..

+0

Es una idea interesante, pero tengo * presentimiento * de que la biblioteca de terceros que los OP intentan utilizar requiere cadenas como entrada (por ejemplo, puede tratarse de una biblioteca de deserialización). –

+0

Sí, tengo la misma sensación :) – Patko

1

Un operador implícito no funcionará aquí?Si Foo es una clase que puedes modificar, he usado algo como esto en el pasado, que también te permite comparar instancias de Foo con una cadena.

public class Foo 
{ 
    public string Name { get; set; } 

    public static implicit operator Foo(string value) 
    { 
    return new Foo { Name = value }; 
    } 
} 

... 
Foo foo = "fooTest"; 
Console.WriteLine("Foo name: {0}", foo.Name); 
... 

Editar: Si tiene que usar changetype continuación, por lo que yo sé que estás de suerte. Si puede modificar la API para usar un TypeConverter, puede usar algo como lo siguiente.

... 
Type type = typeof(Foo); 
object value = "one"; 

var converter = TypeDescriptor.GetConverter(type); 
if (converter.CanConvertFrom(value.GetType())) 
{ 
    object newObject = converter.ConvertFrom(value); 
    Console.WriteLine("Foo value: {0}", newObject.ToString()); 
} 
... 

public class FooConverter : TypeConverter 
{ 
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 
    { 
    return sourceType == typeof(string); 
    } 

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 
    { 
    var name = value as string; 
    if (name != null) 
     return new Foo { Name = name }; 
    else 
     return base.ConvertFrom(context, culture, value); 
    } 
} 

[TypeConverter(typeof(FooConverter))] 
public class Foo 
{ 
    public string Name { get; set; } 

    public override string ToString() 
    { 
    return Name; 
    } 
} 
+1

Creo que el OP entiende cómo funciona el operador implícito. Lo que intenta hacer es diseñar un tipo tal que cuando una biblioteca de terceros llame 'Convert.ChangeType' en una cadena y pase su tipo personalizado como el parámetro' type', se instanciará una instancia de su tipo personalizado. En otras palabras, 'Convert.ChangeType' es una avenida que este código * debe * atravesar (según los requisitos del OP). –

+0

Veo tu punto. No está claro en su comentario, * "Además, tengo la capacidad de cambiar el Convert.ChangeType si hay otra manera de hacer esto." * Si esto significa que puede modificar el origen de la API o no. – MattP

Cuestiones relacionadas