2015-05-23 25 views
17

Sto lavorando a un'implementazione IValueConverter che converte i valori bool?. Per motivi di versatilità, ho deciso di utilizzare TypeConverter per convertire il valore di input in bool?. Dal momento che il suo scopo principale è quello di essere utilizzato come convertitore per i binding XAML, vorrei evitare di generare eccezioni in quanto si traduce in una significativa diminuzione delle prestazioni dell'interfaccia utente. Per fare questo ho provato ad utilizzare TypeConverter.IsValid metodo, ma sono imbattuto in un comportamento singolare, di cui un esempio è mostrato nel seguente codice:Incoerenza nel comportamento del TypeConverter?

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

Forse mi sbaglio, ma mi aspetto il metodo IsValid per tornare false ogni volta che un valore non può essere convertito e true in caso contrario, ma chiaramente questo non è il caso con una stringa vuota e NullableConverter (lo stesso comportamento può essere osservato per altri tipi nullable).

È un errore o piuttosto una scelta di progettazione? E se quest'ultimo, ci sono altri casi simili?

EDIT

Dopo aver ispezionato la source code per NullableConverter Credo di aver trovato la ragione di questo comportamento. Ecco il IsValid realizzazione:

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

Nel mio caso il simpleTypeConverter è di tipo BooleanConverter e, comprensibilmente, restituisce false per string.Empty. D'altra parte, ecco la ConvertFrom attuazione:

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

Ovviamente, string.Empty cade nella seconda if economico, quindi il risultato senza null un'eccezione.

Conoscendo il motivo di questo comportamento, la domanda rimane ancora - è una svista, o è destinato a funzionare in questo modo? Ho inviato uno bug report e pubblicheremo qualsiasi conclusione per uscirne.

+2

Quando provo il tuo esempio 'IsValid' genera un' FormatException' con messaggio 'String non è stato riconosciuto come booleano valido. Tuttavia, la documentazione dice che" Avvio in .NET Framework 4, il metodo IsValid rileva eccezioni dal Metodi CanConvertFrom e ConvertFrom Se il tipo di valore di input causa CanConvertFrom per restituire false, o se il valore di input fa sì che ConvertFrom generi un'eccezione, il metodo IsValid restituisce false. " –

+0

Anche divertente che 'myNullableConverter.ConvertToString (null)' restituisca una stringa vuota. Immagino che 'null' non sia valido per un' Nullable '? –

+1

@MariusBancila Ho appena testato le versioni del framework di targeting del codice che vanno dalla 2.0 alla 4.5.1, e in ogni caso né "ConvertFrom" né "IsValid" hanno generato un'eccezione. È interessante notare che, fino alla versione 3.5 inclusa "IsValid' restituito' true' ... È possibile che "ConvertFrom" "inghiotte" l'eccezione? – Grx70

risposta

1

Ciò che le diverse persone si aspettano in alcune di queste situazioni probabilmente non è lo stesso, ma per me il comportamento dato dal framework in questo caso sembra ragionevole.

Ad esempio: nei seguenti casi, il comportamento mi sembra perfettamente ragionevole.

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 

Dal commento di @ C.Evenhuis, questo è il comportamento che ritengo sia stato giudicato discutibile.

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 sta facendo qualcosa che trovo molto bene. Se noterai, var isNullAString = null is string restituisce false! Ha più senso per me che null venga convertito in una stringa vuota, anche se non è quello che ti aspettavi.

Per quanto riguarda l'ultima parte non indirizzata nella tua domanda ..

Forse mi sbaglio, ma mi aspetto il metodo IsValid restituisca false ogni volta che un valore non può essere convertita e vero il contrario, ma chiaramente non è questo il caso di una stringa vuota e NullableConverter (stesso comportamento può essere osservato per altri tipi nullable).

Credo che sia stata una risposta soddisfacente in un commento precedente, che ha dichiarato

Il metodo IsValid viene utilizzato per convalidare un valore all'interno del tipo, piuttosto che per determinare se il valore può essere convertito nel tipo data . Ad esempio, IsValid può essere utilizzato per determinare se un dato valore è valido per un tipo di enumerazione.

0

Il motivo per cui si verifica il problema è perché String.Empty è una classe "" è un valore letterale. È un ReadOnly Varialble. il che significa che è una variabile NULL di tipo string.