1

I have a reference to an object. I know it conforms to

IDictionary<string, T> 

for some type T. (It may not conform to plain IDictionary, or to IReadyOnlyDictionary). All I know about T is that it descends from object. How can I get its keys, and get the value for a key? (I am fine with having the value be returned as an object, not as a T. I am also fine with never learning what T is.)

What I want to write, but can't, is something like this:

public void SomeMethod(object reallyADict) { // reallyADict implements IDictionary<string, T>.
  foreach (string key in reallyADict.Keys) {
    object value = reallyADict[key];
    // . . .
  }
}

**

Per request, a sample class is below.

using System;
using System.Collections.Generic;
using System.Collections;

namespace My.Collections
{
  public class WrappedDictionary: IDictionary<string, int>
  {
    public WrappedDictionary() {
      this.InnerDictionary = new Dictionary<string, int>{ {"one", 1}, {"two", 2 }};
    }
    private Dictionary<string, int> InnerDictionary { get; set;}

    private ICollection<KeyValuePair<string, int>> InnerCollection {
      get {
        return this.InnerDictionary;
      }
    }


    #region IDictionary implementation
    void IDictionary<string, int>.Add(string key, int value) {
      this.InnerDictionary.Add(key, value);
    }
    bool IDictionary<string, int>.ContainsKey(string key) {
      return this.InnerDictionary.ContainsKey(key);
    }
    bool IDictionary<string, int>.Remove(string key) {
      return this.InnerDictionary.Remove(key);
    }
    bool IDictionary<string, int>.TryGetValue(string key, out int value) {
      return this.InnerDictionary.TryGetValue(key, out value);
    }
    int IDictionary<string, int>.this[string index] {
      get {
        return this.InnerDictionary[index];
      }
      set {
        this.InnerDictionary[index] = value;
      }
    }
    ICollection<string> IDictionary<string, int>.Keys {
      get {
        return this.InnerDictionary.Keys;
      }
    }
    ICollection<int> IDictionary<string, int>.Values {
      get {
        return this.InnerDictionary.Values;
      }
    }
    #endregion
    #region ICollection implementation
    void ICollection<KeyValuePair<string, int>>.Add(KeyValuePair<string, int> item) {
      this.InnerCollection.Add(item);
    }
    void ICollection<KeyValuePair<string, int>>.Clear() {
      this.InnerDictionary.Clear();
    }
    bool ICollection<KeyValuePair<string, int>>.Contains(KeyValuePair<string, int> item) {
      return this.InnerCollection.Contains(item);
    }
    void ICollection<KeyValuePair<string, int>>.CopyTo(KeyValuePair<string, int>[] array, int arrayIndex) {
      this.InnerCollection.CopyTo(array, arrayIndex);
    }
    bool ICollection<KeyValuePair<string, int>>.Remove(KeyValuePair<string, int> item) {
      return this.InnerCollection.Remove(item);
    }
    int ICollection<KeyValuePair<string, int>>.Count {
      get {
        return this.InnerCollection.Count;
      }
    }
    bool ICollection<KeyValuePair<string, int>>.IsReadOnly {
      get {
        return this.InnerCollection.IsReadOnly;
      }
    }
    #endregion
    #region IEnumerable implementation
    IEnumerator<KeyValuePair<string, int>> IEnumerable<KeyValuePair<string, int>>.GetEnumerator() {
      return this.InnerCollection.GetEnumerator();
    }
    #endregion
    #region IEnumerable implementation
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
      return (this as IEnumerable).GetEnumerator();
    }
    #endregion
  }
}
William Jockusch
  • 26,513
  • 49
  • 182
  • 323

1 Answers1

6

Unfortunatly you cannot cast the reallyADict to something like Dictionary<string,T>, because you need a specific type.

And Manfred's comment to use a generic method like

public IEnumerable<T> SomeMethod<T>(Dictionary<string, T> dict)

would be my approach, too. But you stated you really only have the dictionary as object.

So I solved this with Reflection:

public IEnumerable<object> SomeMethod(object reallyADict)
{
    Type genericInterface = reallyADict?.GetType().GetInterface("IDictionary`2");

    PropertyInfo propKeys = genericInterface?.GetProperty("Keys");
    if (propKeys?.GetMethod == null) yield break;

    IEnumerable<string> keys = (IEnumerable<string>)propKeys.GetValue(reallyADict);

    PropertyInfo propIndex = genericInterface.GetProperty("Item");
    if (propIndex?.GetMethod == null) yield break;

    foreach (string key in keys)
        yield return propIndex.GetMethod.Invoke(reallyADict, new object[] { key });
}

This method gets the Keys property from the reallyDict (if there is one) and uses it as an IEnumerable<string>.

Then it iterates over all those keys and uses the indexer property of the underlying dictionary to return the value. The indexer property has the name Item.

Sebastian Brosch
  • 42,106
  • 15
  • 72
  • 87
René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • 1
    @ManfredRadlwimmer No. `GetMethod` is a property of a `PropertyInfo`. It is the `MethodInfo` that represents the getter of that property. In that line, I don't want to _get_ the getter (I already have it in `GetMethod`), I just want to _invoke_ that getter. – René Vogt Aug 05 '16 at 08:08
  • If you are using an older .NET Version, you can use `GetGetMethod()` instead of `GetMethod` (available since 4.5) – Manfred Radlwimmer Aug 05 '16 at 08:08
  • 1
    @ManfredRadlwimmer just checked it, yes, `GetGetMethod()` would return the `MethodInfo` for the getter, but `GetMethod` is just a property that returns the same. You can choose as you like ;) – René Vogt Aug 05 '16 at 08:09
  • Hmm, interesting, when I call reallyADict.GetType().GetRuntimeProperties(), I don't see one with name of "Keys", but I do see one with name of System.Collections.Generic.IDictionary.Keys, where T is the actual value of T . . . – William Jockusch Aug 05 '16 at 08:18
  • @WilliamJockusch I use `GetProperty` not `GetRuntimeProperties()` (though I don't know without looking up what's the difference). Can you show an example _class_ that you call this with? I only tested with a real `Dictionary`, maybe we need a special name instead of `Key` if you have some custom class only implementing `IDictionary`. – René Vogt Aug 05 '16 at 08:24
  • 1
    @WilliamJockusch tested it with a custom implementation of `IDictionary` and it works perfectly. – René Vogt Aug 05 '16 at 08:42
  • @WilliamJockusch Ah, just saw your edit. The problem is that you use explicit interface implementation. That makes things difficult....I'll try to improve the answer when I find the time.. can take a while. – René Vogt Aug 05 '16 at 08:44
  • @WilliamJockusch solved it and update the answer. The trick is to get the interface`'s type and get the `PropertyInfo`s from that type. – René Vogt Aug 05 '16 at 09:02