测试一个对象是否是 C# 中的字典
-
02-07-2019 - |
题
有没有办法测试一个对象是否是字典?
在一种方法中,我试图从列表框中选定的项目中获取值。在某些情况下,列表框可能绑定到字典,但这在编译时是未知的。
我想做类似的事情:
if (listBox.ItemsSource is Dictionary<??>)
{
KeyValuePair<??> pair = (KeyValuePair<??>)listBox.SelectedItem;
object value = pair.Value;
}
有没有办法使用反射在运行时动态地执行此操作?我知道可以使用泛型类型的反射并确定键/值参数,但我不确定是否有办法在检索这些值后执行其余操作。
解决方案
它应该类似于以下内容。我在答案框中写了这个,因此语法可能不完全正确,但我已将其设为 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);
}
其他提示
检查它是否实现了 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。
我知道这个问题很多年前就被问过,但它仍然是公开可见的。
本主题和本主题中提出了一些示例:
确定类型是否为字典[重复]
但有一些不匹配的地方,所以我想分享我的解决方案
简短回答:
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
. 。然后KeyValue集合将继续简单 Objects
.
我相信警告已经到位。
当您测试一个对象是否“是”某个东西时,您正在重新实现类型系统(的一部分)。第一个“is a”通常很快就会跟着第二个,很快你的代码就充满了类型检查,类型系统应该很好地处理这些检查——至少在面向对象的设计中是这样。
当然,我对这个问题的背景一无所知。我确实知道我们自己的代码库中有一个 2000 行文件,可以处理 50 种不同的对象到字符串的转换...:(
if(typeof(IDictionary).IsAssignableFrom(listBox.ItemsSource.GetType()))
{
}