Тестирование, если объект является словарем в C#


14

Есть ли способ проверить, является ли объект словарем?

В методе, в котором я пытаюсь получить значение из выбранного элемента в списке. В некоторых случаях окно списка может быть привязано к словарю, но это не известно во время компиляции.

Я хотел бы сделать что-то похожее на это:

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

Есть ли способ сделать это динамически во время выполнения с помощью отражения? Я знаю, что можно использовать отражение с типичными типами и определять параметры ключа/значения, но я не уверен, есть ли способ сделать остальное после получения этих значений.

10

Это должно быть что-то вроде следующего. Я написал это в поле ответа, поэтому синтаксис может быть не совсем прав, но я сделал его Wiki редактируемым, чтобы кто-нибудь мог его исправить.

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

он, вероятно, лучше работал бы с использованием значения в коробке, чем с помощью отражения. 23 сен. 082008-09-23 19:33:30

  0

Я не уверен, что вы имеете в виду? Вы не можете просто вставить KeyValuePair <TKey,TValue>, чтобы извлечь из него значение. 23 сен. 082008-09-23 19:38:02

  0

Ваше решение сработало. Я изменил оператор if, чтобы продолжить, и просто проверить «IDictionary» (тип вашей части не работал по какой-то причине). Я также изменил «typeof (KeyValuePair <,>)» на «listBox.SelectedItem» 23 сен. 082008-09-23 20:04:40

  0

@Guvante. Фактически вы можете опустить параметры типа при использовании 'typeof' с общим типом. См. Здесь: http://msdn.microsoft.com/en-us/library/b8ytshk6.aspx 23 окт. 132013-10-23 13:28:00

  0

@TimGoodman: Это странно, я попробовал этот синтаксис и получил ошибку компиляции, удалив. 23 окт. 132013-10-23 23:55:52


13

Проверьте, реализует ли он IDictionary.

См. Определение System.Collections.IDictionary, чтобы узнать, что это дает вам.

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

EDIT: Alternative, когда я понял, KeyValuePair не являются преобразуемым DictionaryEntry

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

Это решение использует отражение, но в этом случае вы не должны делать черновую работу, ListBox делает это за вас. Кроме того, если вы, как правило, имеете словари в качестве источников данных, вы можете избежать реселлинга ValueMember все время.

+3

Um, вы знаете IDictionary <TKey,TValue> фактически не реализует интерфейс IDictionary ? Таким образом, это не будет работать для общих словарей. Посмотрите на http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx 23 сен. 082008-09-23 19:32:36

  0

Кроме того, даже если это был словарь <TKey,TValue>, который реализует IDictionary, тогда он реализует IEnumerable <KeyValuePair <TKey,TValue>>, поэтому я ожидаю, что case to DictionaryEntry для отказа. 23 сен. 082008-09-23 19:39:05

  0

Конечно, я имел в виду «бросить в DictionaryEntry» выше. 23 сен. 082008-09-23 19:44:31

  0

Листинг в DictionaryEntry не работает. Пока еще не выяснили решение. 23 сен. 082008-09-23 19:50:26

+1

Ну, чтобы исправить Грег Бич, я просто подтянул словарь <>, и он действительно реализует IDictionary, даже если IDictionary <> не делает. Забыл, что, хотя использование метода индексирования для получения словаря будет работать, я не могу использовать для него SelectedItem. Исправлю, если найду решение без отражения. 24 сен. 082008-09-24 00:58:21


0

Вы можете быть немного более общим и спросить, если он реализует IDictionary. Тогда коллекция KeyValue будет contina plain Objects.

+1

Будьте осторожны с вашей терминологией, ссылаясь на не общие версии, поскольку общие, вероятно, сбивают с толку некоторые люди (я знаю, что я всегда должен исправлять себя: P) 23 сен. 082008-09-23 19:26:37


1

вы можете проверить, реализует ли он IDictionary. Вам просто нужно перечислить класс DictionaryEntry.


0

Я считаю, что предупреждение находится на месте.

Когда вы проверяете, является ли объект «чем-то тем или иным», вы переопределяете (часть) систему типов. Первая «есть» часто быстро сопровождается второй, и вскоре ваш код заполнен проверками типов, которые должны быть очень хорошо обработаны системой типов - по крайней мере, в объектно-ориентированном дизайне.

Конечно, я ничего не знаю о контексте вопроса. Я знаю, файл 2000 строки в собственном коде, который обрабатывает 50 другой объект для преобразования строковых ... :(


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

} 

3

Я знаю, что этот вопрос был задан вопрос много лет назад, но до сих пор видны публично.

Существовали несколько примеров предложенных здесь в этой теме, и в этом одна:
Determine if type is dictionary [duplicate]

но есть несколько несовпадений, поэтому я хочу поделиться своим решением

Короткий ответ:

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

Дольше ответа:
Я считаю, что это является причиной того, почему люди делают ошибки:

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

так пусть у нас есть эти типы:

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

и эти экземпляры:

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

так что если мы будем использовать .IsAssignableFrom():

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

мы не получим ни одного случая

так лучший способ, чтобы получить все интерфейсы и проверить, если любой из них является интерфейс приложения:

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