Testing if an Object is a Dictionary in C#

后端 未结 7 1062
傲寒
傲寒 2020-12-06 10:21

Is there a way to test if an object is a dictionary?

In a method I\'m trying to get a value from a selected item in a list box. In some circumstances, the list box

相关标签:
7条回答
  • 2020-12-06 10:54

    I know this question was asked many years ago, but it is still visible publicly.

    There were few examples proposed here in this topic and in this one:
    Determine if type is dictionary [duplicate]

    but there are few mismatches, so I want to share my solution

    Short answer:

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

    Longer answer:
    I believe this is the reason why people make mistakes:

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

    so let say we have these types:

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

    and these instances:

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

    so if we will use .IsAssignableFrom() method:

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

    we will not get any instance

    so best way is to get all interfaces and check if any of them is dictionary interface:

    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
    
    0 讨论(0)
  • 2020-12-06 11:03

    You could be a little more generic and ask instead if it implements IDictionary. Then the KeyValue collection will contina plain Objects.

    0 讨论(0)
  • 2020-12-06 11:06

    you can check to see if it implements IDictionary. You'll just have to enumerate over using the DictionaryEntry class.

    0 讨论(0)
  • 2020-12-06 11:07

    I believe a warning is at place.

    When you're testing if an object 'is a' something this or that, you're reimplementing (part of) the type system. The first 'is a' is often swiftly followed by a second one, and soon your code is full of type checks, which ought to be very well handled by the type system - at least in an object oriented design.

    Of course, I know nothing of the context of the question. I do know a 2000 line file in our own codebase that handles 50 different object to String conversions... :(

    0 讨论(0)
  • 2020-12-06 11:13

    It should be something like the following. I wrote this in the answer box so the syntax may not be exactly right, but I've made it Wiki editable so anybody can fix up.

    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 讨论(0)
  • 2020-12-06 11:13

    Check to see if it implements IDictionary.

    See the definition of System.Collections.IDictionary to see what that gives you.

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

    EDIT: Alternative when I realized KeyValuePair's aren't castable to DictionaryEntry

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

    This solution uses reflection, but in this case you don't have to do the grunt work, ListBox does it for you. Also if you generally have dictionaries as data sources you may be able to avoid reseting ValueMember all of the time.

    0 讨论(0)
提交回复
热议问题