有没有办法测试一个对象是否是字典?

在一种方法中,我试图从列表框中选定的项目中获取值。在某些情况下,列表框可​​能绑定到字典,但这在编译时是未知的。

我想做类似的事情:

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

}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top