Подтвердить что ты не робот

Добавление членов к динамическому объекту во время выполнения

Я изучаю модель DynamicObject в .NET 4.0. Приложение - это тот, где объект будет описан через какой-то файл text/xml, и программа должна создать объект при чтении этого файла.

С DynamicObject мы можем легко добавлять элементы, учитывая, что мы знаем имя члена априори. Но что, если мы даже не знаем, как добавить имя участника? Есть ли способ сделать эту динамику?

Например, мне нужно создать объект с элементами Property1 ',' Property2 'и другим объектом с' PropertyA 'и' PropertyB ', как описано в файле text/XML. Как я могу создать объект динамически на основе этой информации?

UPDATE Я получил некоторые идеи из этой публикации: http://www.codeproject.com/KB/cs/dynamicincsharp.aspx

Эта реализация позволяет мне сделать что-то вроде следующего:

dynamic d = new PFDynamicChannel();
PFCouplings c = ((PFChannel)d).Coupling;
d.NewProperty = "X";

Причина, по которой я не хочу использовать словарь, - использовать методы TryGetMember и TrySetMember, которые я могу переопределить, в рамках которых я могу поднять события, которые необходимы для программы.

Таким образом, я могу наследовать базовый класс (PFChannel), но я также могу добавлять элементы на лету. Но, моя проблема в том, что я не буду знать новое имя свойства до времени исполнения. И, на самом деле, я не думаю, что динамический объект позволяет мне добавлять новые свойства "на лету". Если это так, как я могу использовать ExpandoObject, чтобы дать мне эту возможность?

4b9b3361

Ответ 1

Если вам нужно только это сделать, вы должны посмотреть ExpandoObject. Если вам нужно это сделать и по-прежнему использовать DynamicObject, вам нужно будет написать код для запоминания значений свойств, в основном... которые вы могли бы потенциально сделать со встроенным ExpandoObject.

Теперь мне непонятно, что вы хотите делать с этим объектом, - вы уверены, что вам нужна динамическая типизация? Будет ли Dictionary<string, object> на самом деле хуже? Это зависит от того, что в конечном итоге будет потреблять объект.

Ответ 2

В соответствии с этим: Добавление свойств и методов в ExpandoObject, динамически!,

... вы можете использовать объект expando в качестве своего владельца значения и передать его в IDictionary < string, object > когда вы хотите добавить динамически именованные свойства.

Пример

dynamic myobject = new ExpandoObject();

IDictionary<string, object> myUnderlyingObject = myobject;

myUnderlyingObject.Add("IsDynamic", true); // Adding dynamically named property

Console.WriteLine(myobject.IsDynamic); // Accessing the property the usual way

Это проверено и распечатает "true" на экране консоли.

Конечно, в вашем случае, когда ваш базовый объект должен наследовать от другого класса, этот пример приведен только для того, чтобы дать вам представление о потенциальной пользовательской реализации.

Может быть, включая объект expando в вашей реализации класса и перенаправление вызовов на tryget и tryset на экземпляр объекта expando в вашем классе?

UPDATE

ЕСЛИ ваш базовый класс происходит от DynamicObject (это означает, что вы можете переопределить все методы TrySet/Get/Invoke), тогда вы также можете использовать словарь внутри себя. В try/set overrides вы сделаете любое событие, которое хотите, и делегируйте настройку во внутренний словарь.

Чтобы добавить новое свойство (или удалить существующий), вы можете переопределить TryInvoke. Когда именем mothod является, например, "AddProperty", и есть один аргумент строки типа, то вы должны добавить новый элемент в словарь с именем аргумента. Аналогично, вы бы динамически определяли "RemoveProperty" и т.д. Вам даже не нужен объект expando.

class MyBaseClass: DynamicObject
{
    // usefull functionality
}

class MyClass: MyBaseClass
{
    Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();

    override bool TryGetMember(...)
    {
       // read the value of the requested property from the dictionary
       // fire any events and return
    }

    override bool TrySetMember(...)
    {
       // set the value of the requested property to the dictionary
       // if the property does not exist,
       // add it to the dictionary (compile time dynamic property naming)
       // fire any events
    }

    override bool TryInvoke(...)
    {
       // check what method is requested to be invoked
       // is it "AddProperty"??
       // if yes, check if the first argument is a string
       // if yes, add a new property to the dictionary
       // with the name given in the first argument (runtime dynamic property naming)
       // if there is also a second argument of type object,
       // set the new property value to that object.

       // if the method to be invoked is "RemoveProperty"
       // and the first argument is a string,
       // remove from the Dictionary the property
       // with the name given in the first argument.

       // fire any events
    }
}

// USAGE
static class Program
{
    public static void Main()
    {
        dynamic myObject = new MyClass();

        myObject.FirstName = "John"; // compile time naming - TrySetMember
        Console.WriteLine(myObject.FirstName); // TryGetMember

        myObject.AddProperty("Salary");  // runtime naming (try invoke "AddProperty" with argument "Salary")
        myObject.Salary = 35000m;
        Console.WriteLine(myObject.Salary); // TryGetMember

        myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11)); // runtime naming (try invoke "AddProperty" with fisrt argument "DateOfBirth" and second argument the desired value)
        Console.WriteLine(myObject.DateOfBirth); // TryGetMember

        myObject.RemoveProperty("FirstName"); // runtime naming (try invoke "RemoveProperty" with argument "FirstName")

        Console.WriteLine(myObject.FirstName); // Should print out empty string (or throw, depending on the desired bahavior) because the "FirstName" property has been removed from the internal dictionary.

    }
}

Конечно, как я уже сказал, это будет работать только в том случае, если ваш базовый класс Derives from DynamicObject.

Ответ 3

Я не уверен, что вы хотите использовать динамический объект в этом случае.

dynamic в С# позволяет делать такие вещи, как:

  dynamic something = GetUnknownObject();
  something.aPropertyThatShouldBeThere = true;

Если вы используете ExpandoObject, вы можете:

  var exp = GetMyExpandoObject();
  exp.APropertyThatDidntExist = "something";

Оба из них позволяют использовать имя свойства, как если бы оно действительно существовало во время компиляции. В вашем случае вам не нужно будет использовать этот синтаксис вообще, поэтому я не уверен, что он принесет вам какую-либо пользу. Вместо этого, почему бы просто не использовать Dictionary<string, object> и:

var props = new Dictionary<string, object>();
   foreach( var someDefinintion in aFile)
   {
      props[ someDefinition.Name] = someDefinition.value;
   }

Поскольку Dictionary<string,object> в основном является объектом expando, но он поддерживает другой синтаксис. Да, я упрощаю, но если вы не используете это из другого кода или связыванием /etc, то это в основном верно.

Ответ 4

Относительно ответа Thanasis Ioannidis относительно варианта "Если ваш базовый класс происходит из DynamicObject", есть более простой способ, просто добавьте метод AddProperty в класс "MyClass" следующим образом:

    class MyClass: MyBaseClass
    {
        Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();

        public void AddProperty(string key, object value){
            dynamicProperties[key] = value;
        }

Затем вы можете использовать

dynamic myObject = new MyClass();
myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11));

Вам не нужно переопределять "TryInvoke".

Ответ 5

dynamic тип базово реализуется как Property Bag. Это означает его словарь ключей (имена свойств) и объектов (здесь небезопасным способом). Я не использую, если вы можете напрямую обращаться к этому словарю, но вы можете использовать словарь самостоятельно.

Черт, Джон Скит был быстрее: (

Ответ 6

        dynamic Dx = new ExpandoObject();
        Dx.Type=type;
        Dx.Mobile=Mobile;