Добавление членов к динамическому объекту во время выполнения - PullRequest
27 голосов
/ 14 октября 2010

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

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

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

ОБНОВЛЕНИЕ Я получил несколько идей из этого поста: http://www.codeproject.com/KB/cs/dynamicincsharp.aspx

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

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

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

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

Ответы [ 5 ]

44 голосов
/ 14 октября 2010

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

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

30 голосов
/ 04 сентября 2012

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

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

Пример

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 get / set вы выполняете любое событие, которое хотите, и делегируете настройку во внутренний словарь.

Чтобы добавить новое свойство (или удалить существующее), вы можете переопределить TryInvoke. Когда имя метода, например, «AddProperty» и есть один аргумент типа string, вы добавляете в свой словарь новый элемент с именем аргумента. Точно так же вы бы динамически определяли «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's 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.

    }
}

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

7 голосов
/ 14 октября 2010

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

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

  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> - это в основном объект расширения, но у него есть поддержка другого синтаксиса. Да, я упрощаю - но если вы не используете это из другого кода или через привязку / etc, то это в основном верно.

1 голос
/ 19 августа 2016

Относительно ответа 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».

0 голосов
/ 14 октября 2010
Тип

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

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

...