tags:

views:

73

answers:

4

I have an abstract base class that holds a Dictionary. I'd like inherited classes to be able to access the dictionary fields using a convenient syntax. Currently I have lots of code like this:

string temp;
int val;
if (this.Fields.TryGetValue("Key", out temp)) {
    if (int.TryParse(temp, out val)) {
        // do something with val...
    }
}

Obviously I can wrap this in utility functions but I'd like to have a cool, convenient syntax for accessing the dictionary fields where I can simply say something like:

int result = @Key;

Is there any way to do something like this in C# (3.5)?

+4  A: 

You could add an indexer to your class and pass the indexer's parameter through to the dictionary.

class Foo
{
    // Initialized elsewhere
    Dictionary<String,String> Fields;

    public Int32 this[String key]
    {
        String temp = null;
        Int32 val = 0;
        if (this.Fields.TryGetValue(key, out temp)) {
            Int32.TryParse(temp, out val);
        }
        return val;
    }
}

Then given an instance of Foo called foo you could do this:

Int32 value = foo["Key"];
Andrew Hare
A: 

How about an extension method?

public static int TryGetInt(this IDictionary dict, string key)
{
    int val;
    if (dict.Contains(key))
    {
        if (int.TryParse((string)dict[key], out val))
            return val;
        else
            throw new Exception("Value is not a valid integer.");    
    }

    throw new Exception("Key not found.");
}
Codesleuth
Why would you use methods that deliberately avoid exceptions, just to later throw less descriptive exceptions? This whole method body could be written `return int.Parse(dict[key]);` and would have the same behaviour in the case of invalid values, but more descriptive exceptions.
Greg Beech
That's a good point. Why raise exceptions at all if the underlying model will raise these exceptions in the same circumstances? Bottom line here is that this answer was not designed to be the perfect solution; it was made just to wrap the original OP's code into a extension method. You're quite right to notice the unnecessary exceptions thrown here, but surely if you can see this, you can notice why they're there in my answer.
Codesleuth
How is that "a cool, convenient syntax"?
Mark Brackett
An extension method attaches to any object that matches the first parameter. I'm not required to declare a class and convert my existing code. I'd say it's more convenient than you suggest.
Codesleuth
A: 

The closer you can get to a nice syntax is using extension methods:

public static class MyDictExtensionMethods
{
  public static T Get<T>(this Dictionary<string, object> dict, string key)
    where T: IConvertible
  {
    object tmp;
    if (!dict.TryGetValue(key, out tmp))
      return default(T);
    try {
      return (T) Convert.ChangeType(tmp, typeof(T));
    } catch (Exception) {
      return default(T);
    }
  }
}

Usage:

int val = this.Fields.Get<int>("Key");

You can then create additional overloads for specific types (i.e.: types that does not implement IConvertible and need specific conversion).

Fábio Batista
A: 

Assuming that it's not always an int you want (if it is, then why isn't it a Dictionary<string, int>?) - I think something like this works and gets pretty close:

int i = @int["Key"];
string s = @string["Key"];
object o = @object["Key"];

This combines the fact that identifiers can be prefixed with @ (it's usually optional, but it's required if your identifier is a reserved keyword, like int or string) with the default indexed parameter from Andrew Hare's answer.

It does require another class to be used to get the indexing - though if you wanted to use parens instead of square brackets for the key name, you could use methods instead:

int i = @value<int>("Key");

Implementation would be something like:

class DerivedClass : BaseClass {
   void Main() {
      int i = @int["Key"];
   }
}

abstract class BaseClass {
   private Dictionary<string, string> D { get; set; }

   protected Indexer<int> @int = new Indexer<int>(s => int.Parse(s), this);
   protected Indexer<string> @string = new Indexer<string>(s => s, this);
   protected Indexer<object> @object = new Indexer<object>(s => (object)s, this);

   protected class Indexer<T> {
       public T this[string key] {
          get { return this.Convert(this.BaseClass.D[key]); }
       }

       private T Convert(string value) { get; set; }
       private BaseClass { get; set; }

       public Indexer(Func<T, string> c, BaseClass b) {
          this.Convert = c;
          this.BaseClass = b;
       }
   }    
}

Or, the method route:

class DerivedClass : BaseClass {
   void Main() {
      int i = @value<int>("key");
   }
}

abstract class BaseClass {
   private Dictionary<string, string> D { get; set; }

   protected T @value<T>(string key) {
      string s = this.D[s];
      return Convert.ChangeType(s, typeof(T));
   }
}  

After reading through the language spec - if you're not tied to @, _ is a legal identifier. Combine that with indexers and you get:

int i = _["key"];
Mark Brackett