在运行时动态添加C#属性

115

我知道有一些问题针对这个进行了讨论,但答案通常是推荐使用字典或参数集合,并且在我的情况下不起作用。

我正在使用一个库,通过反射来处理具有属性的对象。这适用于已定义的类,以及动态类。我需要更进一步,做出以下操作:

public static object GetDynamicObject(Dictionary<string,object> properties) {
    var myObject = new object();
    foreach (var property in properties) {
        //This next line obviously doesn't work... 
        myObject.AddProperty(property.Key,property.Value);
    }
    return myObject;
}

public void Main() {
    var properties = new Dictionary<string,object>();
    properties.Add("Property1",aCustomClassInstance);
    properties.Add("Property2","TestString2");

    var myObject = GetDynamicObject(properties);

    //Then use them like this (or rather the plug in uses them through reflection)
    var customClass = myObject.Property1;
    var myString = myObject.Property2;

}

这个库可以与动态变量类型一起正常工作,并且可以手动分配属性。但是,我不知道预先会添加多少或者哪些属性。

3个回答

134

你看过ExpandoObject吗?

参见:https://learn.microsoft.com/zh-cn/dotnet/api/system.dynamic.expandoobject

来自MSDN:

ExpandoObject类使您能够在运行时添加和删除其实例的成员,并设置和获取这些成员的值。该类支持动态绑定,使您能够使用标准语法(例如 sampleObject.sampleMember)而不是更复杂的语法(例如 sampleObject.GetAttribute("sampleMember"))。

使您能够做一些很酷的事情,比如:

dynamic dynObject = new ExpandoObject();
dynObject.SomeDynamicProperty = "Hello!";
dynObject.SomeDynamicAction = (msg) =>
    {
        Console.WriteLine(msg);
    };

dynObject.SomeDynamicAction(dynObject.SomeDynamicProperty);

根据您实际的代码,您可能更感兴趣的是:

public static dynamic GetDynamicObject(Dictionary<string, object> properties)
{
    return new MyDynObject(properties);
}

public sealed class MyDynObject : DynamicObject
{
    private readonly Dictionary<string, object> _properties;

    public MyDynObject(Dictionary<string, object> properties)
    {
        _properties = properties;
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _properties.Keys;
    }

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

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_properties.ContainsKey(binder.Name))
        {
            _properties[binder.Name] = value;
            return true;
        }
        else
        {
            return false;
        }
    }
}

那样你只需要:

var dyn = GetDynamicObject(new Dictionary<string, object>()
    {
        {"prop1", 12},
    });

Console.WriteLine(dyn.prop1);
dyn.prop1 = 150;

继承自DynamicObject类可以让你为处理这些动态成员请求想出自己的策略,注意:这里可能会有风险。编译器无法验证很多动态调用,并且你也无法得到智能提示,所以要谨记这一点。


60

感谢@Clint提供的精彩答案:

我只想强调使用Expando Object解决这个问题是多么容易:

var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
foreach (var property in properties) {
    dynamicObject.Add(property.Key,property.Value);
}      

1
这里的“properties”是什么意思,“foreach (var property in properties)”?例如,我有一个名为Student的类,并且我想根据我的JSON响应添加属性。 - singhswat
2
@singhswat 这是基于问题中的代码,它是 Dictionary<string, object> 类型。 - Stefan Golubović

11
你可以将你的JSON字符串反序列化为字典,然后添加新属性,然后再次序列化它。
var jsonString = @"{}";

var jsonDoc = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);

jsonDoc.Add("Name", "Khurshid Ali");

Console.WriteLine(JsonSerializer.Serialize(jsonDoc));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接