Pergunta

Existe uma maneira de testar se um objeto é um dicionário?

Em um método que eu estou tentando obter um valor de um item selecionado em uma caixa lista. Em algumas circunstâncias, a caixa de lista pode ser ligado a um dicionário, mas isso não é conhecido em tempo de compilação.

Eu gostaria de fazer algo semelhante a isto:

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

Existe uma maneira de fazer isso dinamicamente em tempo de execução usando reflexão? Eu sei que é possível usar a reflexão com tipos genéricos e determinar os parâmetros de chave / valor, mas não tenho certeza se há uma maneira de fazer o resto depois esses valores são recuperados.

Foi útil?

Solução

Deve ser algo como o seguinte. Eu escrevi isso na caixa de resposta assim que a sintaxe pode não ser exatamente certo, mas eu fiz isso Wiki editável para que qualquer um pode corrigir-se.

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

Outras dicas

Verifique para ver se ele implementa IDictionary.

Veja a definição de System.Collections.IDictionary para ver o que lhe dá.

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

EDIT: Alternativa quando me dei conta de KeyValuePair não são passível de conversão para DictionaryEntry

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

Esta solução usa reflexão, mas, neste caso, você não tem que fazer o trabalho pesado, ListBox faz isso por você. Além disso, se você geralmente tem dicionários como fontes de dados que você pode ser capaz de evitar zerando ValueMember todo o tempo.

Eu sei que esta pergunta foi feita há muitos anos, mas ainda é visível publicamente.

Houve alguns exemplos aqui proposto neste tópico e neste:
Determinar se o tipo é dicionário [duplicado]

mas existem alguns desencontros, então eu quero compartilhar minha solução

Resposta curta:

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())))

resposta mais longa:
Creio que esta é a razão por que as pessoas cometem erros:

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

então vamos dizer que temos estes tipos:

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

e essas instâncias:

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

por isso, se vamos usar o método .IsAssignableFrom ():

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

não teremos qualquer instância

para melhor maneira é fazer com que todas as interfaces e verificar se algum deles é a interface dicionário:

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

você pode verificar para ver se ele implementa IDictionary . Você só terá que enumerar sobre usando o DictionaryEntry classe.

Você poderia ser um pouco mais genérico e perguntar em vez se ele implementa IDictionary. Em seguida, a coleção KeyValue vai Contina Objects simples.

Eu acredito que um aviso está no lugar.

Quando você está testando se um objeto 'é um' algo isto ou aquilo, você está reimplementar (parte de) o sistema de tipos. O primeiro 'é um' é muitas vezes rapidamente seguido por um segundo, e logo seu código está cheio de verificações de tipo, que deve ser muito bem tratados pelo sistema tipo -. Pelo menos em um projeto orientado a objetos

É claro, eu não sei nada sobre o contexto da pergunta. Eu sei um arquivo linha 2000, em nossa própria base de código que lida com 50 objeto diferente para conversões de cadeia ...: (

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top