c# - ¿Inconsistencia en el comportamiento de TypeConverter?
(2)
Estoy trabajando en una implementación de IValueConverter
que convertiría bool?
valores. En aras de la versatilidad, he decidido utilizar TypeConverter
para convertir el valor de entrada en bool?
. Dado que su propósito principal es ser utilizado como un convertidor para los enlaces XAML, me gustaría evitar que se produzcan excepciones, ya que resulta en una disminución significativa del rendimiento de la interfaz de usuario. Para hacerlo, intenté usar el método TypeConverter.IsValid
, pero encontré un comportamiento peculiar, cuyo ejemplo se muestra en el siguiente código:
//returned converter is a NullableConverter
var converter = TypeDescriptor.GetConverter(typeof(bool?));
//this method returns false
converter.IsValid(string.Empty);
//yet this method returns null without throwing an exception
converter.ConvertFrom(string.Empty);
Quizás me equivoque, pero esperaría que el método IsValid
devuelva false
siempre que un valor no pueda convertirse y sea true
otra manera, pero claramente ese no es el caso con una cadena vacía y NullableConverter
(se puede observar el mismo comportamiento para otros tipos anulables) .
¿Es esto un error o más bien una elección de diseño? Y si esto último, ¿hay otros casos similares?
EDITAR
Después de inspeccionar el código fuente de NullableConverter
, creo que he encontrado la razón de este comportamiento. Aquí está la implementación de IsValid
:
public override bool IsValid(ITypeDescriptorContext context, object value) {
if (simpleTypeConverter != null) {
object unwrappedValue = value;
if (unwrappedValue == null) {
return true; // null is valid for nullable.
}
else {
return simpleTypeConverter.IsValid(context, unwrappedValue);
}
}
return base.IsValid(context, value);
}
En mi caso, el simpleTypeConverter
es de tipo BooleanConverter
y, comprensiblemente, devuelve false
para string.Empty
. Por otro lado, aquí está la implementación de ConvertFrom
:
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
if (value == null || value.GetType() == this.simpleType) {
return value;
}
else if (value is String && String.IsNullOrEmpty(value as String)) {
return null;
}
else if (this.simpleTypeConverter != null) {
object convertedValue = this.simpleTypeConverter.ConvertFrom(context, culture, value);
return convertedValue;
}
else {
return base.ConvertFrom(context, culture, value);
}
}
Obviamente, string.Empty
cae en la segunda instrucción if
, de ahí el resultado null
sin una excepción.
Sabiendo la razón de este comportamiento, la pregunta sigue siendo: ¿es un descuido o está destinado a funcionar de esta manera? He enviado un informe de error y publicaré cualquier conclusión que salga de él.
La razón por la que tiene el problema es porque String.Empty es una clase vs "" es un Literal. Es un Varialble ReadOnly. lo que significa que es una variable nula de tipo cadena.
Lo que las diferentes personas esperan en algunas de estas situaciones probablemente no sea lo mismo, pero para mí el comportamiento dado por el marco en este caso parece razonable.
Por ejemplo: en los siguientes casos, el comportamiento me parece perfectamente razonable.
var converter = TypeDescriptor.GetConverter(typeof(bool?));
bool? nullableBool1 = converter.ConvertFrom(string.Empty); // returns null
bool? nullableBool2 = converter.ConvertFrom("true"); // returns true
bool? nullableBool3 = converter.ConvertFrom("false"); // returns false
bool? nullableBool4 = converter.ConvertFromString(string.Empty); // returns null
bool? nullableBool5 = converter.ConvertFromString("true"); // returns true
bool? nullableBool6 = converter.ConvertFromString("false"); // returns false
Del comentario de @C.Evenhuis, este es el comportamiento que creo que se consideró cuestionable.
var converter = TypeDescriptor.GetConverter(typeof(bool?));
var string1 = converter.ConvertToString(null); // returns ""
var string2 = converter.ConvertToString(true); // returns "true"
var string3 = converter.ConvertToString(false); // returns "false"
ConvertToString
está haciendo algo que me parece muy bueno. Si nota, var isNullAString = null is string
devuelve false
! Tiene más sentido para mí que el nulo se convierta en una cadena vacía, incluso si eso no es lo que esperabas.
En cuanto a la última parte no abordada en su pregunta ...
Quizás me equivoque, pero esperaría que el método IsValid devuelva falso siempre que un valor no pueda convertirse y sea verdadero de otra manera, pero claramente ese no es el caso con una cadena vacía y NullableConverter (se puede observar el mismo comportamiento para otros tipos anulables) .
Creo que esto fue respondido satisfactoriamente en un comentario anterior, que declaró
El método IsValid se usa para validar un valor dentro del tipo en lugar de determinar si el valor se puede convertir al tipo dado. Por ejemplo, se puede usar IsValid para determinar si un valor dado es válido para un tipo de enumeración.