получить значение словаря, используя оператор. (точка), используя динамическое ключевое слово, это возможно? - PullRequest
3 голосов
/ 15 ноября 2011

У меня есть следующий класс:

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(), "..");
            }
        }
    }

Мне нужно получить значение словаря с помощью точечного операдотора, потому что класс, для которого я установил класс в качестве аргументов, использует ключевое слово dynamic для "обхода" класса.

например:

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

если существует способ создания динамических переменных, что-то вроде:

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

делает переменную с именем arg и значением .. как public член foo класса.

В любом случае, для решения этой проблемы очень важно. Заранее спасибо.

Ответы [ 4 ]

7 голосов
/ 15 ноября 2011

Вы можете иметь Foo наследовать от 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);
    }
}

Чтобы использовать его, вы можете использовать dynamic для хранения экземпляра Foo:

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

Имейте в виду, что вы потеряете безопасность типов, поскольку вам придется использовать dynamic - ваш вариант использования должен действительно оправдать этот недостаток - обычно есть лучший подход.

4 голосов
/ 15 ноября 2011

Я думаю, вы должны использовать DynamicObject .Если вы используете более старую версию фреймворка, единственным вариантом будет Reflection.Emit

Динамический работает примерно так:

// 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;
}
3 голосов
/ 15 ноября 2011

Другой вариант - использовать ExpandoObject класс , если вы хотите напрямую выставить член Data, как в вашем примере. Это делает код проще, если вам не нужно определять конкретные операции, которые потребуют наследования 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(), "..");
        }
    }
}

Использование:

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);
}

Обратите внимание, что я произвел приведение к словарю и добавил «d», хотя я мог бы также назначить его напрямую: foo.Data.d = "!". Единственная разница в том, что вы можете не знать заранее, какие имена полей у вас есть, и первый пример позволяет вам настроить ExpandoObject на основе динамического ввода, тогда как последний полезен, когда вы уже знаете, какое имя поля использовать.

2 голосов
/ 15 ноября 2011

В .NET 4 это точное поведение реализуется ExpandoObject классом:

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);
...