3

I have the following class:

public class foo
    {
        public Dictionary<string, string> data = new Dictionary<string, string>();

        public foo(params object[] args)
        {
            foreach (object arg in args)
            {
                data.Add(arg.ToString(), "..");
            }
        }
    }

I need get the value of dictionary using the dot operadotor it's because the class that I set the class as arguments use the dynamic keyword to "walk" on the class.

for example:

 var args = new[] {"a","b","c"};
 var Foo = new foo(args);
 var baa = Foo.data.a;
 Console.Write(baa); // .. 

if exists an way to make dynamic variables, something like:

public foo(params object[] args) {
foreach (object arg in args) {
  var name = (string) arg;
  var value = "..";
  MakeVariable(name, value);  
}
}

makes an variable named of arg and the value .. as public member of foo class.

anyway differents to solve this is very appreciated. Thanks in advance.

The Mask
  • 17,007
  • 37
  • 111
  • 185

4 Answers4

7

You can have Foo inherit from DynamicObject:

public class Foo : DynamicObject
{
    private Dictionary<string, string> data = new Dictionary<string, string>();

    public Foo(params object[] args)
    {
        foreach (object arg in args)
        {
            data.Add(arg.ToString(), "..");
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (data.ContainsKey(binder.Name))
        {
            result = data[binder.Name];
            return true;
        }
        return base.TryGetMember(binder, out result);
    }
}

To use it you can use dynamic to hold an instance of Foo:

var args= new[] { "a", "b", "c" };
dynamic foo = new Foo(args);
var myA = foo.a; //returns ".."

Keep in mind that you will lose type safety since you have to use dynamic - your use case should really justify this disadvantage - usually there is a better approach.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
4

I think you should use DynamicObject. If you are using an older version of the framework the only option is Reflection.Emit

The dynamic works something like this

// If you try to get a value of a property 
// not defined in the class, this method is called.
public override bool TryGetMember(
    GetMemberBinder binder, out object result)
{
    // Converting the property name to lowercase
    // so that property names become case-insensitive.
    string name = binder.Name.ToLower();

    // If the property name is found in a dictionary,
    // set the result parameter to the property value and return true.
    // Otherwise, return false.
    return dictionary.TryGetValue(name, out result);
}

// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
    SetMemberBinder binder, object value)
{
    // Converting the property name to lowercase
    // so that property names become case-insensitive.
    dictionary[binder.Name.ToLower()] = value;

    // You can always add a value to a dictionary,
    // so this method always returns true.
    return true;
}
parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
3

Another option is to use the ExpandoObject class if you want to directly expose the Data member, as in your example. This keeps the code simpler if you don't need to define specific operations that would require inheriting DynamicObject.

public class Foo
{
    public dynamic Data = new ExpandoObject();
    public Foo(params object[] args)
    {
        var dataDict = (IDictionary<string, object>)Data;
        foreach (var obj in args)
        {
            dataDict.Add(obj.ToString(), "..");
        }
    }
}

Usage:

var foo = new Foo("a", "b", "c");
Console.WriteLine(foo.Data.a);    

((IDictionary<string, object>)foo.Data).Add("d", "!");

foreach (var item in foo.Data)
{
    Console.WriteLine("{0} : {1}", item.Key, item.Value);
}

Notice that I cast to a dictionary and added "d" although I could've also assigned it directly: foo.Data.d = "!". The only difference is you may not know ahead of time what field names you have, and the former example allows you to setup the ExpandoObject based on dynamic input, whereas the latter is useful when you already know what field name to use.

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
2

In .NET 4 this exact behavior is implemented by ExpandoObject class:

public class Foo
{
    private readonly ExpandoObject _dict = new ExpandoObject();

    public dynamic Data
    {
        get { return _dict; }
    }

    public Foo(params object[] args)
    {
         foreach (var arg in args)
             _dict.Add(arg.ToString(), "..");
    }
}

var foo = new Foo("a", "b", "c");
foo.Data.x = 3.14;
Console.Write(foo.Data.a);
Bojan Resnik
  • 7,320
  • 28
  • 29