Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Treat Object Like Dictionary of Properties in C#

I want to be able to access property values in an object like a dictionary, using the name of the property as a key. I don't really care if the values are returned as objects, so Dictionary<string, object> is fine. This is the intended usage:

object person = new { Name: "Bob", Age: 45 };
IDictionary<string, object> lookup = new PropertyDictionary(person);
string name = (string)person["Name"];
person["Age"] = (int)person["Age"] + 1; // potentially editable

I was about to implement my own class for this, but then I started noticing classes like DynamicObject implement the IDictionary interface, which made think this was already being done for me somewhere.

What I want is similar to the functionality used by ASP.NET MVC that allows using anonymous types to set HTML tag attributes. I have a lot of classes that use dictionaries as data sources, but most of the time I should be able to pass in objects as well.

Since this is for a general-purpose library, I thought I would create a reusable class that simply decorated an object with the IDictionary interface. It will save me from creating an explosion of overloads.

like image 224
Travis Parks Avatar asked Mar 11 '12 04:03

Travis Parks


People also ask

Can we use object as key in dictionary?

Almost any type of value can be used as a dictionary key in Python. You can even use built-in objects like types and functions. However, there are a couple restrictions that dictionary keys must abide by.

What is dictionary object in C#?

Dictionary is a collection of keys and values in C#. Dictionary is included in the System. Collection. Generics namespace.

Can a dictionary be a property C#?

Item[] Property. This property is used to get or set the value associated with the specified key in the Dictionary. Here, key is the Key of the value to get or set.

Can a dictionary have different value types?

One can only put one type of object into a dictionary. If one wants to put a variety of types of data into the same dictionary, e.g. for configuration information or other common data stores, the superclass of all possible held data types must be used to define the dictionary.


2 Answers

I don't believe there is a built-in .Net type like this already in the .Net framework. It seems like you really want to create an object that behaves a lot like a Javascript object. If so then deriving from DynamicObject may be the right choice. It allows you to create an object which when wrapped with dynamic allows you to bind directly obj.Name or via the indexer obj["Name"].

public class PropertyBag : DynamicObject {
  private object _source;
  public PropertyBag(object source) {
    _source = source;
  }
  public object GetProperty(string name) {  
    var type = _source.GetType();
    var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    return property.GetValue(_source, null);
  }
  public override bool TryGetMember(GetMemberBinder binder, out object result) {
    result = GetProperty(binder.Name);
    return true;
  }
  public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
    result = GetProperty((string)indexes[0]);
    return true;
  }
}

You can use this to wrap any type and use both the indexer and name syntax to get the properties

var student = new Student() { FirstName = "John", LastName = "Doe" };
dynamic bag = new PropertyBag(student);
Console.WriteLine(bag["FirstName"]);  // Prints: John
Console.WriteLine(bag.FirstName);     // Prints: John
like image 194
JaredPar Avatar answered Oct 16 '22 11:10

JaredPar


I have this extension method, probably the simplest it can get:

public static Dictionary<string, object> ToPropertyDictionary(this object obj)
{
    var dictionary = new Dictionary<string, object>();
    foreach (var propertyInfo in obj.GetType().GetProperties())
        if (propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0)
            dictionary[propertyInfo.Name] = propertyInfo.GetValue(obj, null);
    return dictionary;
}

Now you can do:

object person = new { Name = "Bob", Age = 45 };
var lookup = person.ToPropertyDictionary();
string name = (string)lookup["Name"];
lookup["Age"] = (int)lookup["Age"] + 1; // indeed editable

Note:

  1. that this dictionary is case-sensitive (you can trivially extend it passing the right StringComparer).

  2. that it ignores indexers (which are also properties) but it's up to you to work on it.

  3. that the method is not generic considering it doesn't help boxing because internally it calls obj.GetType, so it boxes anyway at that point.

  4. that you get only the "readable" properties (otherwise you dont get the values held in it). Since you want it to be "writable" as well then you should use CanWrite flag as well.

like image 8
nawfal Avatar answered Oct 16 '22 13:10

nawfal