动态对象
动态对象是一种特殊类型的对象,其成员(属性和方法)的解析是在运行时而不是编译时确定的。这种类型的对象允许你在编写代码时不需要提前知道对象的成员,而是可以在运行时根据需要动态添加和调用成员。其相关类型主要是在System.Dynamic命名空间中,虽然其下有很多的类,但是最常用的就是ExpandoObject
类和DynamicObject
类。
ExpandoObject
介绍
ExpandoObject
是C#中实现的一种动态类型,它允许在运行时动态地添加和删除成员。与静态类型不同,ExpandoObject的成员在编译时不需要提前定义,而是可以在运行时根据需要进行扩展。
ExpandoObject
显式实现了IDictionary<string, object?>
接口,可以通过转换成IDictionary<string, object?>
类型来进行成员的操作。
ExpandoObject
显式实现了INotifyPropertyChanged
接口,可以接收成员更改的通知。
ExpandoObject
是线程安全的,在添加/修改/删除成员时有互斥锁。
ExpandoObject
的实现其实是数组,将key和value分别放在ExpandoClass
和ExpandoData
两个类中,这两个类的核心都是数组。
示例
下面是一个简单的示例,演示如何使用ExpandoObject
来创建使用一个动态类型的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| dynamic obj = new ExpandoObject();
obj.Name = "名称"; obj.age = 20;
obj.Read = (Action)(() => Debug.WriteLine("test...")); obj.Read();
((IDictionary<string, object?>)obj).Remove("Name");
((INotifyPropertyChanged)obj).PropertyChanged += new PropertyChangedEventHandler((sender, e) => { Console.WriteLine($"{e.PropertyName}变更了。"); });
|
DynamicObject
介绍
DynamicObject
是一个特殊的基类,它允许您创建动态对象,并在运行时动态地处理成员访问、方法调用和运算符操作。他的存在使得C#编程更加灵活,特别是在与动态语言进行交互或者需要在运行时生成对象成员的情况下。
DynamicObject
的唯一一个构造函数是protected的,所以需要子类继承他并实现其动态行为。
DynamicObject
的核心可重写方法如下:
- TryGetMember:用于在访问动态对象的成员时进行处理。
- TrySetMember:用于在设置动态对象的成员时进行处理。
- TryInvokeMember:用于在调用动态对象的方法时进行处理。
- TryInvoke:用于在调用动态对象本身时进行处理。
- TryConvert:用于在将动态对象转换为其他类型时进行处理。
- TryBinaryOperation:用于在进行二元操作时进行处理。
- TryUnaryOperation:用于在进行一元操作时进行处理。
示例
下面是一个简单的示例,演示如何继承DynamicObject
后来创建使用一个动态类型的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
public class CustomDynamicObject : DynamicObject { private readonly Dictionary<string, object?> caches = [];
public override IEnumerable<string> GetDynamicMemberNames() { return caches.Keys; }
public override DynamicMetaObject GetMetaObject(Expression parameter) { return base.GetMetaObject(parameter); }
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result) { switch (binder.Operation) { case ExpressionType.Add: break; } result = new CustomDynamicObject(); return true; }
public override bool TryConvert(ConvertBinder binder, out object? result) { if (binder.ReturnType == typeof(int)) { } result = this; return true; }
public override bool TryCreateInstance(CreateInstanceBinder binder, object?[]? args, [NotNullWhen(true)] out object? result) { return base.TryCreateInstance(binder, args, out result); }
public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) { return base.TryDeleteIndex(binder, indexes); }
public override bool TryDeleteMember(DeleteMemberBinder binder) { return base.TryDeleteMember(binder); }
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object? result) { if (indexes.Length == 1 && indexes[0] != null) { result = caches.GetValueOrDefault(indexes[0].ToString()!); } else { result = null; } return true; }
public override bool TryGetMember(GetMemberBinder binder, out object? result) { return caches.TryGetValue(binder.Name, out result); }
public override bool TryInvoke(InvokeBinder binder, object?[]? args, out object? result) { if (args?.Length == 1 && args[0] != null) { return caches.TryGetValue(args[0]!.ToString()!, out result); } else { result = null; } return true; }
public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args, out object? result) { result = false; if (binder.Name.Equals("remove", StringComparison.CurrentCultureIgnoreCase) && args?.Length == 1 && args[0] != null) { result = caches.Remove(args[0]!.ToString()!); } return (bool)result; }
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value) { if (indexes.Length == 1 && indexes[0] != null) { caches.Add(indexes[0].ToString()!, value); } return true; }
public override bool TrySetMember(SetMemberBinder binder, object? value) { return caches.TryAdd(binder.Name, value); }
public override bool TryUnaryOperation(UnaryOperationBinder binder, out object? result) { result = 1; return true; } }
|
使用方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| dynamic obj = new CustomDynamicObject();
obj = obj + 1;
ExpandoObject obj2 = (ExpandoObject)obj;
obj["name"] = "张三";
string name = obj["name"];
obj.name = "李四";
string name1 = obj.name;
string name2 = obj("name");
obj.Remove("name");
obj++;
|