Есть ли способ проверить, является ли объект словарем?
В методе я пытаюсь получить значение из выбранного элемента в списке. В некоторых случаях список может быть привязан к словарю, но это неизвестно во время компиляции.
Я хотел бы сделать что-то подобное:
if (listBox.ItemsSource is Dictionary<??>)
{
KeyValuePair<??> pair = (KeyValuePair<??>)listBox.SelectedItem;
object value = pair.Value;
}
Есть ли способ сделать это динамически во время выполнения с помощью отражения? Я знаю, что можно использовать отражение с универсальными типами и определять параметры ключа / значения, но я не уверен, есть ли способ сделать все остальное после того, как эти значения будут извлечены.
Проверьте, реализует ли он IDictionary.
См. Определение System.Collections.IDictionary, чтобы узнать, что это дает.
if (listBox.ItemsSource is IDictionary)
{
DictionaryEntry pair = (DictionaryEntry)listBox.SelectedItem;
object value = pair.Value;
}
Обновлено: Альтернатива, когда я понял, что 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.
Кроме того, даже если это был Dictionary <TKey, TValue>, который действительно реализует IDictionary, тогда он реализует IEnumerable <KeyValuePair <TKey, TValue >>, поэтому я бы ожидал, что в случае с DictionaryEntry произойдет сбой.
Преобразование в DictionaryEntry не удается. До сих пор не нашел решения.
Что ж, чтобы исправить Грега Бича, я просто открыл Dictionary <>, и он действительно реализует IDictionary, даже если IDictionary <> этого не делает. Забыл, что хотя использование метода индексации для получения DictionaryEntry будет работать, я не могу применить к нему SelectedItem. Исправлю, если найду решение без отражения.
Вы могли бы быть немного более общим и вместо этого спросить, реализует ли он IDictionary
. Тогда коллекция KeyValue будет содержать обычный Objects
.
Будьте осторожны со своей терминологией, упоминание неуниверсальных версий как универсальных, вероятно, сбивает с толку некоторых людей (я знаю, что всегда должен исправляться: P)
вы можете проверить, реализует ли он IDictionary. Вам просто нужно будет перечислить, используя класс DictionaryEntry.
Должно получиться примерно следующее. Я написал это в поле для ответа, поэтому синтаксис может быть не совсем правильным, но я сделал его редактируемым в 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);
}
вероятно, было бы лучше использовать значение в штучной упаковке, чем использование отражения.
Я не уверен, что вы имеете в виду? Вы не можете просто упаковать KeyValuePair <TKey, TValue>, чтобы извлечь из него значение.
Ваше решение сработало. Я изменил оператор if, чтобы продолжить, и просто протестировал "is IDictionary" (ваша часть typeof по какой-то причине не сработала). Я также изменил "typeof (KeyValuePair <,>)" на "listBox.SelectedItem"
@Guvante Фактически, вы можете опустить параметры типа при использовании typeof
с универсальным типом. Смотрите здесь: msdn.microsoft.com/en-us/library/b8ytshk6.aspx
@TimGoodman: Это странно, я попробовал этот синтаксис и получил ошибку компиляции, удаление.
Я считаю, что предупреждение на месте.
Когда вы проверяете, является ли объект чем-то тем или иным, вы повторно реализуете (часть) системы типов. За первым 'is a' часто быстро следует второй, и вскоре ваш код полон проверок типов, которые должны очень хорошо обрабатываться системой типов - по крайней мере, в объектно-ориентированном дизайне.
Конечно, я ничего не знаю о контексте вопроса. Я знаю файл из 2000 строк в нашей собственной кодовой базе, который обрабатывает 50 различных объектов для преобразования String ... :(
if (typeof(IDictionary).IsAssignableFrom(listBox.ItemsSource.GetType()))
{
}
Я знаю, что этот вопрос задавали много лет назад, но он до сих пор виден публично.
Было предложено несколько примеров здесь, в этой теме и в этой:
Определите, является ли тип словарным [дубликат]
но есть несколько несоответствий, поэтому я хочу поделиться своим решением
Короткий ответ:
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
Вы знаете, что IDictionary <TKey, TValue> на самом деле не реализует интерфейс IDictionary? Так что для универсальных словарей это не сработает. Посмотрите на msdn.microsoft.com/en-us/library/s4ys34ea.aspx