在运行时将成员添加到动态对象

我正在探索.NET 4.0中的DynamicObject模型。 应用程序是通过某种text / xml文件描述对象的应用程序,程序必须在读取该文件时创建对象。

使用DynamicObject,我们可以轻松添加成员,因为我们知道成员的名称是先验的。 但是,如果我们甚至不知道要添加的成员的名称呢? 有没有办法让这种动态变化?

例如,假设我需要使用成员’Property1’,’Property2’创建一个对象,使用’PropertyA’创建另一个对象,并使用text / XML文件描述’PropertyB’。 如何根据此信息动态创建对象?

更新我从这篇文章中得到了一些想法: http : //www.codeproject.com/KB/cs/dynamicincsharp.aspx

这个实现允许我做类似以下的事情:

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

我不想使用字典的原因是使用TryGetMember和TrySetMember方法,我可以覆盖,我可以在其中引发对程序至关重要的事件。

这样,我可以inheritance基类(PFChannel),但我也可以动态添加成员。 但是,我的问题是,在运行时之前我不会知道新的属性名称。 而且,我实际上并不认为动态对象允许我动态添加新属性。 如果是这种情况,我如何利用ExpandoObject来提供这种能力呢?

如果您只需要这样做,您应该查看ExpandoObject 。 如果你需要这样做并且仍然使用DynamicObject ,你需要编写代码来记住属性值,基本上……你可能会使用嵌入式ExpandoObject

之后我不清楚你想用这个对象做什么 – 你确定你需要动态打字吗? Dictionary实际上会更糟吗? 这取决于以后基本上会消耗对象的内容。

根据这个: 动态地向ExpandoObject添加属性和方法! ,

…您可以使用expando对象作为值持有者,并在想要添加动态命名属性时将其强制转换为IDictionary

 dynamic myobject = new ExpandoObject(); IDictionary myUnderlyingObject = myobject; myUnderlyingObject.Add("IsDynamic", true); // Adding dynamically named property Console.WriteLine(myobject.IsDynamic); // Accessing the property the usual way 

这经过测试,将在控制台屏幕上打印出“true”。

当然,在您的情况下,您的底层对象必须从另一个类inheritance,这个示例只是为了让您了解潜在的自定义实现。

也许在类实现中包含一个expando对象,并将调用重定向到tryget和tryset到类中expando对象的实例?

UPDATE

如果您的基类派生自DynamicObject(意味着您可以覆盖所有TrySet / Get / Invoke方法),那么您也可以在内部使用字典。 在try get / set覆盖中,您可以执行任何您想要的事件触发,并将设置委托给内部字典。

要添加新属性(或删除现有属性),您可以覆盖TryInvoke。 例如,当mothod名称是“AddProperty”并且有一个类型为string的参数时,您将在字典中添加一个带有参数名称的新项。 类似地,您将动态定义“RemoveProperty”等。您甚至不需要expando对象。

 class MyBaseClass: DynamicObject { // usefull functionality } class MyClass: MyBaseClass { Dictionary dynamicProperties = new Dictionary(); 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时才会有效。

在这种情况下,我不确定您是否要使用动态对象。

c#中的dynamic允许你做以下事情:

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

如果使用ExpandoObject ,则可以:

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

这两个都允许您使用propertyname,就好像它在编译时实际存在一样 。 在您的情况下,您根本不需要使用此语法,因此我不确定它会给您带来任何好处。 相反,为什么不使用Dictionary和:

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

因为Dictionary基本上是一个expando对象 – 但它支持不同的语法。 是的,我正在简化 – 但如果你不是从其他代码或通过绑定/等使用它,那么这基本上是正确的。

与Thanasis Ioannidis相关的答案有关变体“如果您的基类派生自DynamicObject”,有一种更简单的方法,只需在“MyClass”类中添加方法AddProperty,如下所示:

  class MyClass: MyBaseClass { Dictionary dynamicProperties = new Dictionary(); public void AddProperty(string key, object value){ dynamicProperties[key] = value; } 

然后你可以使用

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

您不需要任何覆盖“TryInvoke”。

dynamic类型基本上实现为Property Bag。 这意味着它的键(属性名称)和对象(这里是非类型安全的方式)的字典。 如果您可以直接访问此词典,我就不会使用,但您可以自己使用词典。

该死的,Jon Skeet更快:(