Domanda

Esiste un modo per verificare se un oggetto è un dizionario?

In un metodo sto cercando di ottenere un valore da un elemento selezionato in una casella di riepilogo. In alcune circostanze, la casella di riepilogo potrebbe essere associata a un dizionario, ma questo non è noto al momento della compilazione.

Vorrei fare qualcosa di simile a questo:

if (listBox.ItemsSource is Dictionary<??>)
{
    KeyValuePair<??> pair = (KeyValuePair<??>)listBox.SelectedItem;
    object value = pair.Value;
}

C'è un modo per farlo dinamicamente in fase di esecuzione usando la riflessione? So che è possibile usare la riflessione con tipi generici e determinare i parametri chiave / valore, ma non sono sicuro che ci sia un modo per fare il resto dopo che questi valori sono stati recuperati.

È stato utile?

Soluzione

Dovrebbe essere qualcosa di simile al seguente. L'ho scritto nella casella di risposta, quindi la sintassi potrebbe non essere esattamente corretta, ma l'ho resa modificabile da Wiki in modo che chiunque possa sistemare.

if (listBox.ItemsSource.IsGenericType && 
    typeof(IDictionary<,>).IsAssignableFrom(listBox.ItemsSource.GetGenericTypeDefinition()))
{
    var method = typeof(KeyValuePair<,>).GetProperty("Value").GetGetMethod();
    var item = method.Invoke(listBox.SelectedItem, null);
}

Altri suggerimenti

Verifica se implementa IDictionary.

Vedi la definizione di System.Collections.IDictionary per vedere cosa ti dà.

if (listBox.ItemsSource is IDictionary)
{
    DictionaryEntry pair = (DictionaryEntry)listBox.SelectedItem;
    object value = pair.Value;
}

Modifica Alternativa quando ho capito che KeyValuePair non è utilizzabile su DictionaryEntry

if (listBox.DataSource is IDictionary)
{
     listBox.ValueMember = "Value";
     object value = listBox.SelectedValue;
     listBox.ValueMember = ""; //If you need it to generally be empty.
}

Questa soluzione usa la riflessione, ma in questo caso non devi fare il lavoro grugnito, ListBox lo fa per te. Inoltre, se in genere hai dizionari come origini dati, potresti essere in grado di evitare di reimpostare ValueMember sempre.

So che questa domanda è stata posta molti anni fa, ma è ancora visibile pubblicamente.

Ci sono stati alcuni esempi proposti qui in questo argomento e in questo:
Determina se il tipo è dizionario [duplicato]

ma ci sono alcune discrepanze, quindi voglio condividere la mia soluzione

Risposta breve:

var dictionaryInterfaces = new[]
{
    typeof(IDictionary<,>),
    typeof(IDictionary),
    typeof(IReadOnlyDictionary<,>),
};

var dictionaries = collectionOfAnyTypeObjects
    .Where(d => d.GetType().GetInterfaces()
        .Any(t=> dictionaryInterfaces
            .Any(i=> i == t || t.IsGenericType && i == t.GetGenericTypeDefinition())))

Risposta più lunga:
Credo che questo sia il motivo per cui le persone commettono errori:

//notice the difference between IDictionary (interface) and Dictionary (class)
typeof(IDictionary<,>).IsAssignableFrom(typeof(IDictionary<,>)) // true 
typeof(IDictionary<int, int>).IsAssignableFrom(typeof(IDictionary<int, int>)); // true

typeof(IDictionary<int, int>).IsAssignableFrom(typeof(Dictionary<int, int>)); // true
typeof(IDictionary<,>).IsAssignableFrom(typeof(Dictionary<,>)); // false!! in contrast with above line this is little bit unintuitive

quindi diciamo che abbiamo questi tipi:

public class CustomReadOnlyDictionary : IReadOnlyDictionary<string, MyClass>
public class CustomGenericDictionary : IDictionary<string, MyClass>
public class CustomDictionary : IDictionary

e queste istanze:

var dictionaries = new object[]
{
    new Dictionary<string, MyClass>(),
    new ReadOnlyDictionary<string, MyClass>(new Dictionary<string, MyClass>()),
    new CustomReadOnlyDictionary(),
    new CustomDictionary(),
    new CustomGenericDictionary()
};

quindi se useremo il metodo .IsAssignableFrom ():

var dictionaries2 = dictionaries.Where(d =>
    {
        var type = d.GetType();
        return type.IsGenericType && typeof(IDictionary<,>).IsAssignableFrom(type.GetGenericTypeDefinition());
    }); // count == 0!!

non avremo alcuna istanza

quindi il modo migliore è quello di ottenere tutte le interfacce e verificare se una di esse è l'interfaccia del dizionario:

var dictionaryInterfaces = new[]
{
    typeof(IDictionary<,>),
    typeof(IDictionary),
    typeof(IReadOnlyDictionary<,>),
};

var dictionaries2 = dictionaries
    .Where(d => d.GetType().GetInterfaces()
        .Any(t=> dictionaryInterfaces
            .Any(i=> i == t || t.IsGenericType && i == t.GetGenericTypeDefinition()))) // count == 5

puoi verificare se implementa IDictionary . Dovrai solo enumerare utilizzando DictionaryEntry classe.

Potresti essere un po 'più generico e chiedere invece se implementa IDictionary . Quindi la raccolta KeyValue contina Oggetti .

Credo che sia presente un avviso.

Quando stai testando se un oggetto 'è un' qualcosa questo o quello, stai reimplementando (parte del) sistema di tipi. Il primo 'è un' è spesso seguito rapidamente da un secondo, e presto il tuo codice è pieno di controlli di tipo, che dovrebbero essere gestiti molto bene dal sistema di tipi - almeno in un design orientato agli oggetti.

Certo, non so nulla del contesto della domanda. Conosco un file di 2000 righe nel nostro codebase che gestisce 50 conversioni diverse da oggetto a stringa ... :(

if(typeof(IDictionary).IsAssignableFrom(listBox.ItemsSource.GetType()))
{

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top