在C#中测试一个对象是否为字典。

18
有没有一种方法可以测试一个对象是否是字典?
在一个方法中,我试图从列表框中选择的项中获取一个值。在某些情况下,列表框可能绑定到一个字典上,但这在编译时是不知道的。
我想做类似于这样的事情:
if (listBox.ItemsSource is Dictionary<??>)
{
    KeyValuePair<??> pair = (KeyValuePair<??>)listBox.SelectedItem;
    object value = pair.Value;
}

使用反射在运行时动态地完成这个任务有什么方法吗?我知道可以使用泛型类型和确定键值参数,但是在检索到这些值之后是否有办法完成其余部分,我并不确定。

8个回答

14

检查它是否实现了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。


4
您知道IDictionary<TKey,TValue>实际上并没有实现IDictionary接口吗?因此,这对于泛型字典将不起作用。请查看http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx。 - Greg Beech
此外,即使它是实现了IDictionary的Dictionary<TKey,TValue>,它也实现了IEnumerable<KeyValuePair<TKey,TValue>>,所以我预计将DictionaryEntry用于它会失败。 - Greg Beech
强制转换为DictionaryEntry失败了。我还没有找到解决方案。 - Bob Wintemberg
1
好的,为了纠正Greg Beech的说法,我刚刚查了一下Dictionary<>,它实际上实现了IDictionary,即使IDictionary<>没有实现。 我忘记了使用索引方法来获取DictionaryEntry会起作用,但我无法将SelectedItem强制转换为它。如果我找到非反射解决方案,我会修复它。 - Guvante

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);
}

使用装箱值可能比使用反射性能更好。 - Darren Kopp
我不确定你的意思是什么?你不能只是将一个KeyValuePair<TKey,TValue>装箱以从中提取值。 - Greg Beech
你的解决方案可行。我将if语句改为直接测试“is IDictionary”(你的typeof部分出了一些问题)。我还将“typeof(KeyValuePair<,>)”更改为“listBox.SelectedItem”。 - Bob Wintemberg
@Guvante 实际上,在使用泛型类型的 typeof 时,您可以省略类型参数。请参见此处:http://msdn.microsoft.com/zh-cn/library/b8ytshk6.aspx - Tim Goodman
@TimGoodman:很奇怪,我尝试了那个语法,但是编译错误了,只好移除了。 - Guvante

5

我知道这个问题已经问了很多年,但它仍然可以公开查看。

在这个主题中和这个主题中有几个提出的例子:
确定类型是否为字典[重复]

但是有一些不匹配,所以我想分享我的解决方案

简短回答:

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

1

1

我来自确定类型是否为字典,那里没有一个答案能够充分解决我的问题。

这里最接近的答案来自Lukas Klusis,但没有提供一个IsDictionary(Type type)方法。以下是该方法,受他的回答启发:

private static Type[] dictionaryInterfaces = 
{
  typeof(IDictionary<,>),
  typeof(System.Collections.IDictionary),
  typeof(IReadOnlyDictionary<,>),
};

public static bool IsDictionary(Type type) 
{
   return dictionaryInterfaces
    .Any(dictInterface =>
        dictInterface == type || // 1
        (type.IsGenericType && dictInterface == type.GetGenericTypeDefinition()) || // 2
        type.GetInterfaces().Any(typeInterface => // 3
                                 typeInterface == dictInterface ||
                                 (typeInterface.IsGenericType && dictInterface == typeInterface.GetGenericTypeDefinition())));
}

// 1涉及到的是属性public System.Collections.IDictionary MyProperty {get; set;}

// 2涉及到的是属性public IDictionary<SomeObj, SomeObj> MyProperty {get; set;}

// 3(也就是第二个.Any)涉及到的是类型实现了dictionaryInterfaces中的任意一个类型的情况。

其他答案存在问题,因为它们只解决了#3,而没有考虑#1和#2。这是可以理解的,因为获取和检查属性类型可能并不是常见的场景。但是,如果你像我一样,这种情况是你使用的一部分,那么就可以参考这个方法!


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

}

0
你可以更加通用一些,询问它是否实现了 IDictionary 接口。这样 KeyValue 集合将包含普通的 Objects

1
小心术语,将非通用版本称为通用可能会让一些人感到困惑(我知道我总是要纠正自己:P) - Guvante

0

我认为需要给出警告。

当你测试一个对象是否“是”某个东西时,你正在重新实现(部分)类型系统。第一个“is a”通常很快就会跟随第二个,“is a”,很快你的代码就充满了类型检查,这些应该由类型系统非常好地处理-至少在面向对象设计中是这样。

当然,我不知道问题的上下文。但我知道我们自己代码库中有一个2000行的文件,处理50个不同的对象转换为字符串... :(


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接