├── .assets
├── nupkg-icon.docx
└── nupkg-icon.png
├── .github
└── workflows
│ └── unit-tests.yml
├── .gitignore
├── DynamicBinder.Test
├── DynamicBinder.Test.csproj
├── DynamicBinderTest.cs
├── LateBinderTest.cs
└── TestTargetClass.cs
├── DynamicBinder.sln
├── DynamicBinder
├── Binder.cs
├── DynamicBinder.cs
├── DynamicBinder.csproj
├── DynamicBinderExtension.cs
└── LateBinder.cs
├── LICENSE
├── README.md
├── RELEASE-NOTES.txt
└── _dist
└── .gitkeep
/.assets/nupkg-icon.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsakamoto/dynamicbinder/88208c45e2e5013dec3c8417c2828118338ff813/.assets/nupkg-icon.docx
--------------------------------------------------------------------------------
/.assets/nupkg-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsakamoto/dynamicbinder/88208c45e2e5013dec3c8417c2828118338ff813/.assets/nupkg-icon.png
--------------------------------------------------------------------------------
/.github/workflows/unit-tests.yml:
--------------------------------------------------------------------------------
1 | name: unit tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | jobs:
8 | deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | # Checkout the code
12 | - uses: actions/checkout@v3
13 |
14 | # Install .NET SDK
15 | - name: Setup .NET SDK
16 | uses: actions/setup-dotnet@v2
17 | with:
18 | dotnet-version: 8.0.*
19 |
20 | # Perform unit tests
21 | - name: Perform unit tests
22 | run: dotnet test DynamicBinder.Test --nologo
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.suo
2 | *.user
3 | /packages/
4 | /TestResult/
5 | bin/
6 | obj/
7 | .vs/
8 | _dist/*.nupkg
--------------------------------------------------------------------------------
/DynamicBinder.Test/DynamicBinder.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | DynamicBinderTest
6 | false
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/DynamicBinder.Test/DynamicBinderTest.cs:
--------------------------------------------------------------------------------
1 | using Toolbelt;
2 | using Toolbelt.DynamicBinderExtension;
3 | using Xunit;
4 |
5 | namespace DynamicBinderTest;
6 |
7 | public class DynamicBinderTest
8 | {
9 | [Fact]
10 | public void CreateInstanceT_for_public_constructor()
11 | {
12 | var obj = DynamicBinder.CreateInstance();
13 | ((string)obj.PropA).Is("Fizz");
14 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
15 | }
16 |
17 | [Fact]
18 | public void CreateInstanceT_for_private_constructor_1()
19 | {
20 | var obj = DynamicBinder.CreateInstance("Lorem");
21 | ((string)obj.PropA).Is("Lorem");
22 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
23 | }
24 |
25 | [Fact]
26 | public void CreateInstanceT_for_private_constructor_2()
27 | {
28 | var obj = DynamicBinder.CreateInstance("Ipsum", DateTime.Parse("2022/03/27 15:49:10"));
29 | ((string)obj.PropA).Is("Ipsum");
30 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2022/03/27 15:49:10");
31 | }
32 |
33 | [Fact]
34 | public void CreateInstance_for_public_constructor()
35 | {
36 | var obj = DynamicBinder.CreateInstance(typeof(TestTargetClass));
37 | ((string)obj.PropA).Is("Fizz");
38 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
39 | }
40 |
41 | [Fact]
42 | public void CreateInstance_for_private_constructor_1()
43 | {
44 | var obj = DynamicBinder.CreateInstance(typeof(TestTargetClass), "Lorem");
45 | ((string)obj.PropA).Is("Lorem");
46 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
47 | }
48 |
49 | [Fact]
50 | public void CreateInstance_for_private_constructor_2()
51 | {
52 | var obj = DynamicBinder.CreateInstance(typeof(TestTargetClass), "Ipsum", DateTime.Parse("2022/03/27 15:49:10"));
53 | ((string)obj.PropA).Is("Ipsum");
54 | ((DateTime)obj._FieldB).ToString("yyyy/MM/dd HH:mm:ss").Is("2022/03/27 15:49:10");
55 | }
56 |
57 | [Fact]
58 | public void CreateInstance_with_DynamicObject_by_Dynamic()
59 | {
60 | // Given
61 | var testTarget = DynamicBinder.CreateInstance();
62 | var subItemA = testTarget.CreateSubItem1();
63 |
64 | // When
65 | var subItemB = DynamicBinder.CreateInstance(subItemA);
66 |
67 | // Then
68 | ((string)subItemB.Name).Is("Jude");
69 | ((int)subItemB.Value).Is(47);
70 | }
71 |
72 | [Fact]
73 | public void CallOverloadedPrivateInstanceMethod_by_Dynamic()
74 | {
75 | object obj = new TestTargetClass();
76 |
77 | var actual1 = (string)obj.ToDynamic().MethodC("King");
78 | actual1.Is("Method-C by string: King");
79 |
80 | var actual2 = (int)obj.ToDynamic().MethodC(29);
81 | actual2.Is(29);
82 | }
83 |
84 | [Fact]
85 | public void GetAndSetPrivateInstanceProperty_by_Dynamic()
86 | {
87 | object obj = new TestTargetClass();
88 | var actual1 = (string)obj.ToDynamic().PropA;
89 | actual1.Is("Fizz");
90 |
91 | obj.ToDynamic().PropA = "Dynamic Buzz";
92 |
93 | var actual2 = (string)obj.ToDynamic().PropA;
94 | actual2.Is("Dynamic Buzz");
95 | }
96 |
97 | [Fact]
98 | public void GetAndSetPrivateInstanceField_by_Dynamic()
99 | {
100 | object obj = new TestTargetClass();
101 | var actual1 = (DateTime)obj.ToDynamic()._FieldB;
102 | actual1.Is(DateTime.Parse("2014/02/13 14:27:56"));
103 |
104 | obj.ToDynamic()._FieldB = DateTime.Parse("2016/12/10 03:07:04");
105 |
106 | var actual2 = (DateTime)obj.ToDynamic()._FieldB;
107 | actual2.Is(DateTime.Parse("2016/12/10 03:07:04"));
108 | }
109 |
110 | [Fact]
111 | public void CallOverloadedPrivateStaticMethod_by_Dynamic()
112 | {
113 | var binder = DynamicBinder.Create();
114 |
115 | var actual1 = (string)binder.MethodF("Emperor", 46);
116 | actual1.Is("Method-F(int): Emperor / 46");
117 |
118 | var actual2 = (string)binder.MethodF("Strap", 19.28);
119 | actual2.Is("Method-F(double): Strap / 19.28");
120 | }
121 |
122 | [Fact]
123 | public void GetAndSetPrivateStaticProperty_by_Dynamic()
124 | {
125 | var binder = DynamicBinder.Create(typeof(TestTargetClass));
126 | var actual1 = (string)binder.PropD;
127 | actual1.IsNull();
128 |
129 | try
130 | {
131 | binder.PropD = "Dynamic FizzBuzz";
132 |
133 | var actual2 = (string)binder.PropD;
134 | actual2.Is("Dynamic FizzBuzz");
135 | }
136 | finally
137 | {
138 | binder.PropD = null;
139 | }
140 | }
141 |
142 | [Fact]
143 | public void GetAndSetPrivateStaticField_by_Dynamic()
144 | {
145 | var obj = new TestTargetClass();
146 | var binder = DynamicBinder.Create(obj.GetType());
147 | var actual1 = (string)binder._FieldE;
148 | actual1.Is("Static Foo");
149 |
150 | try
151 | {
152 | binder._FieldE = "Static Dynamic Bar";
153 |
154 | var actual2 = (string)binder._FieldE;
155 | actual2.Is("Static Dynamic Bar");
156 | }
157 | finally
158 | {
159 | binder._FieldE = "Static Foo";
160 | }
161 | }
162 |
163 | [Fact]
164 | public void RetrieveClassObject_by_Dynamic()
165 | {
166 | var binder = DynamicBinder.Create();
167 |
168 | var subItem = binder.GetSubItem();
169 | ((string)subItem.Name).Is("John");
170 | ((int)subItem.Value).Is(40);
171 | }
172 |
173 | [Fact]
174 | public void GetAndSetPropOfClassObject_by_Dynamic()
175 | {
176 | object obj = new TestTargetClass();
177 | string name1 = obj.ToDynamic().PropH.Name;
178 | int value1 = obj.ToDynamic().PropH.Value;
179 | name1.Is("Alice");
180 | value1.Is(29);
181 |
182 | obj.ToDynamic().PropH.Name = "Bob";
183 | obj.ToDynamic().PropH.Value = 37;
184 | string name2 = obj.ToDynamic().PropH.Name;
185 | int value2 = obj.ToDynamic().PropH.Value;
186 | name2.Is("Bob");
187 | value2.Is(37);
188 |
189 | string name3 = obj.ToDynamic().PropG.Name;
190 | int value3 = obj.ToDynamic().PropG.Value;
191 | name3.Is("Sam");
192 | value3.Is(33);
193 |
194 | obj.ToDynamic().PropG = new TestTargetClass.SubItemClass1("Baby", 0);
195 | string name4 = obj.ToDynamic().PropG.Name;
196 | int value4 = obj.ToDynamic().PropG.Value;
197 | name4.Is("Baby");
198 | value4.Is(0);
199 | }
200 |
201 | public enum Gender { Male, Female }
202 |
203 | [Fact]
204 | public void GetPropOfAnonymousType_by_Dynamic()
205 | {
206 | object obj = new
207 | {
208 | Person = new
209 | {
210 | Gender = Gender.Male,
211 | Birthday = DateTime.Parse("1970/01/15"),
212 | ProgramingLangs = new[] { "C#", "F#" }
213 | },
214 | Count = 1
215 | };
216 |
217 | Gender gender = obj.ToDynamic().Person.Gender;
218 | DateTime birthday = obj.ToDynamic().Person.Birthday;
219 | int birthdayYear = obj.ToDynamic().Person.Birthday.Year;
220 | string birthdayMonth = obj.ToDynamic().Person.Birthday.ToLocalTime().Month.ToString();
221 | string[] programingLangs = obj.ToDynamic().Person.ProgramingLangs;
222 | int count = obj.ToDynamic().Count;
223 |
224 | gender.Is(Gender.Male);
225 | birthday.Is(DateTime.Parse("1970/01/15"));
226 | birthdayYear.Is(1970);
227 | birthdayMonth.Is("1");
228 | programingLangs.Is("C#", "F#");
229 | count.Is(1);
230 | }
231 |
232 | [Fact]
233 | public void RetrieveReturnValueOfMethod()
234 | {
235 | object obj = new TestTargetClass();
236 | var retval = obj.ToDynamic().CreateSubItem1() as DynamicBinder;
237 | retval.Object.ToString().Is("Good Job!");
238 | }
239 |
240 | // -------------------------
241 |
242 | [Fact]
243 | public void CallOverloadedPrivateInstanceMethod_of_DerivedClass_by_Dynamic()
244 | {
245 | object obj = new DerivedTestTargetClass();
246 |
247 | var actual1 = (string)obj.ToDynamic().MethodC("King");
248 | actual1.Is("Method-C by string: King");
249 |
250 | var actual2 = (int)obj.ToDynamic().MethodC(29);
251 | actual2.Is(29);
252 | }
253 |
254 | [Fact]
255 | public void GetAndSetPrivateInstanceProperty_of_DerivedClass_by_Dynamic()
256 | {
257 | object obj = new DerivedTestTargetClass();
258 | var actual1 = (string)obj.ToDynamic().PropA;
259 | actual1.Is("Fizz");
260 |
261 | obj.ToDynamic().PropA = "Dynamic Buzz";
262 |
263 | var actual2 = (string)obj.ToDynamic().PropA;
264 | actual2.Is("Dynamic Buzz");
265 | }
266 |
267 | [Fact]
268 | public void GetAndSetPrivateInstanceField_of_DerivedClass_by_Dynamic()
269 | {
270 | object obj = new DerivedTestTargetClass();
271 | var actual1 = (DateTime)obj.ToDynamic()._FieldB;
272 | actual1.Is(DateTime.Parse("2014/02/13 14:27:56"));
273 |
274 | obj.ToDynamic()._FieldB = DateTime.Parse("2016/12/10 03:07:04");
275 |
276 | var actual2 = (DateTime)obj.ToDynamic()._FieldB;
277 | actual2.Is(DateTime.Parse("2016/12/10 03:07:04"));
278 | }
279 |
280 | [Fact]
281 | public void CallOverloadedPrivateStaticMethod_of_DerivedClass_by_Dynamic()
282 | {
283 | var binder = DynamicBinder.Create();
284 |
285 | var actual1 = (string)binder.MethodF("Emperor", 46);
286 | actual1.Is("Method-F(int): Emperor / 46");
287 |
288 | var actual2 = (string)binder.MethodF("Strap", 19.28);
289 | actual2.Is("Method-F(double): Strap / 19.28");
290 | }
291 |
292 | [Fact]
293 | public void GetAndSetPrivateStaticProperty_of_DerivedClass_by_Dynamic()
294 | {
295 | var binder = DynamicBinder.Create(typeof(DerivedTestTargetClass));
296 | var actual1 = (string)binder.PropD;
297 | actual1.IsNull();
298 |
299 | try
300 | {
301 | binder.PropD = "Dynamic FizzBuzz";
302 |
303 | var actual2 = (string)binder.PropD;
304 | actual2.Is("Dynamic FizzBuzz");
305 | }
306 | finally
307 | {
308 | binder.PropD = null;
309 | }
310 | }
311 |
312 | [Fact]
313 | public void GetAndSetPrivateStaticField_of_DerivedClass_by_Dynamic()
314 | {
315 | var obj = new DerivedTestTargetClass();
316 | var binder = DynamicBinder.Create(obj.GetType());
317 | var actual1 = (string)binder._FieldE;
318 | actual1.Is("Static Foo");
319 |
320 | try
321 | {
322 | binder._FieldE = "Static Dynamic Bar";
323 |
324 | var actual2 = (string)binder._FieldE;
325 | actual2.Is("Static Dynamic Bar");
326 | }
327 | finally
328 | {
329 | binder._FieldE = "Static Foo";
330 | }
331 | }
332 |
333 | [Fact]
334 | public void CallPrivateInstanceMethod_with_ref_and_out_Argument_by_Dynamic()
335 | {
336 | object obj = new TestTargetClass();
337 |
338 | var y = 6;
339 | obj.ToDynamic().MethodG(5, ref y, out int z);
340 |
341 | y.Is(7);
342 | z.Is(30);
343 | }
344 |
345 | [Fact]
346 | public void CallPrivateStaticMethod_with_ref_and_out_Argument_by_Dynamic()
347 | {
348 | var binder = DynamicBinder.Create();
349 |
350 | var y = 6;
351 | binder.MethodH(5, ref y, out int z);
352 |
353 | y.Is(16);
354 | z.Is(10);
355 | }
356 |
357 | [Fact]
358 | public void RetrieveObject_and_UseIt_by_Dynamic()
359 | {
360 | // Given
361 | var binder = DynamicBinder.Create();
362 | var subItem = binder.GetSubItem();
363 |
364 | // When
365 | string name = binder.GetNameOfSubItem(subItem);
366 |
367 | // Then
368 | name.Is("John");
369 | ((int)subItem.Value).Is(40);
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/DynamicBinder.Test/LateBinderTest.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using Toolbelt;
3 | using Toolbelt.DynamicBinderExtension;
4 | using Xunit;
5 |
6 | namespace DynamicBinderTest;
7 |
8 | public class LateBinderTest
9 | {
10 | [Fact]
11 | public void CreateInstanceT_for_public_constructor()
12 | {
13 | var obj = LateBinder.CreateInstance();
14 | obj.Prop["PropA"].Is("Fizz");
15 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
16 | }
17 |
18 | [Fact]
19 | public void CreateInstanceT_for_private_constructor_1()
20 | {
21 | var obj = LateBinder.CreateInstance("Lorem");
22 | obj.Prop["PropA"].Is("Lorem");
23 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
24 | }
25 |
26 | [Fact]
27 | public void CreateInstanceT_for_private_constructor_2()
28 | {
29 | var obj = LateBinder.CreateInstance("Ipsum", DateTime.Parse("2022/03/27 15:49:10"));
30 | obj.Prop["PropA"].Is("Ipsum");
31 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2022/03/27 15:49:10");
32 | }
33 |
34 | [Fact]
35 | public void CreateInstance_for_public_constructor()
36 | {
37 | var obj = LateBinder.CreateInstance(typeof(TestTargetClass));
38 | obj.Prop["PropA"].Is("Fizz");
39 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
40 | }
41 |
42 | [Fact]
43 | public void CreateInstance_for_private_constructor_1()
44 | {
45 | var obj = LateBinder.CreateInstance(typeof(TestTargetClass), "Lorem");
46 | obj.Prop["PropA"].Is("Lorem");
47 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2014/02/13 14:27:56");
48 | }
49 |
50 | [Fact]
51 | public void CreateInstance_for_private_constructor_2()
52 | {
53 | var obj = LateBinder.CreateInstance(typeof(TestTargetClass), "Ipsum", DateTime.Parse("2022/03/27 15:49:10"));
54 | obj.Prop["PropA"].Is("Ipsum");
55 | ((DateTime)obj.Field["_FieldB"]).ToString("yyyy/MM/dd HH:mm:ss").Is("2022/03/27 15:49:10");
56 | }
57 |
58 | [Fact]
59 | public void CreateInstance_with_DynamicObject_by_Dynamic()
60 | {
61 | // Given
62 | var testTarget = LateBinder.CreateInstance();
63 | var subItemA = testTarget.Call("CreateSubItem1").ToLateBind();
64 |
65 | // When
66 | var subItemB = LateBinder.CreateInstance(subItemA);
67 |
68 | // Then
69 | subItemB.Prop["Name"].Is("Jude");
70 | subItemB.Prop["Value"].Is(47);
71 | }
72 |
73 | [Fact]
74 | public void CallOverloadedPrivateInstanceMethod_by_LateBinder()
75 | {
76 | object obj = new TestTargetClass();
77 |
78 | obj.ToLateBind().Call("MethodC", "Adelie")
79 | .IsInstanceOf()
80 | .Is("Method-C by string: Adelie");
81 |
82 | obj.ToLateBind().Call("MethodC", 27)
83 | .IsInstanceOf()
84 | .Is(27);
85 | }
86 |
87 | [Fact]
88 | public void GetAndSetPrivateInstanceProperty_by_LateBinder()
89 | {
90 | object obj = new TestTargetClass();
91 | obj.ToLateBind().Prop["PropA"]
92 | .IsInstanceOf()
93 | .Is("Fizz");
94 |
95 | obj.ToLateBind().Prop["PropA"] = "Buzz";
96 |
97 | obj.ToLateBind().Prop["PropA"]
98 | .Is("Buzz");
99 | }
100 |
101 | [Fact]
102 | public void GetAndSetPrivateInstanceField_by_LateBinder()
103 | {
104 | object obj = new TestTargetClass();
105 | obj.ToLateBind().Field["_FieldB"]
106 | .IsInstanceOf()
107 | .Is(DateTime.Parse("2014/02/13 14:27:56"));
108 |
109 | obj.ToLateBind().Field["_FieldB"] = DateTime.Parse("2015/11/09 02:06:03");
110 |
111 | obj.ToLateBind().Field["_FieldB"]
112 | .Is(DateTime.Parse("2015/11/09 02:06:03"));
113 | }
114 |
115 | [Fact]
116 | public void CallOverloadedPrivateStaticMethod_by_LateBinder()
117 | {
118 | var binder = LateBinder.Create(typeof(TestTargetClass));
119 |
120 | binder.Call("MethodF", "Gentoo", 18)
121 | .IsInstanceOf()
122 | .Is("Method-F(int): Gentoo / 18");
123 |
124 | binder.Call("MethodF", "RockHoper", 31.4)
125 | .IsInstanceOf()
126 | .Is("Method-F(double): RockHoper / 31.4");
127 | }
128 |
129 | [Fact]
130 | public void GetAndSetPrivateStaticProperty_by_LateBinder()
131 | {
132 | var binder = LateBinder.Create();
133 | binder.Prop["PropD"]
134 | .IsNull();
135 |
136 | try
137 | {
138 | binder.Prop["PropD"] = "FizzBuzz";
139 |
140 | binder.Prop["PropD"]
141 | .IsInstanceOf()
142 | .Is("FizzBuzz");
143 | }
144 | finally
145 | {
146 | binder.Prop["PropD"] = null;
147 | }
148 | }
149 |
150 | [Fact]
151 | public void GetAndSetPrivateStaticField_by_LateBinder()
152 | {
153 | object obj = new TestTargetClass();
154 | var binder = LateBinder.Create(obj.GetType());
155 |
156 | binder.Field["_FieldE"]
157 | .IsInstanceOf()
158 | .Is("Static Foo");
159 |
160 | try
161 | {
162 | binder.Field["_FieldE"] = "Static Bar";
163 |
164 | binder.Field["_FieldE"]
165 | .Is("Static Bar");
166 | }
167 | finally
168 | {
169 | binder.Field["_FieldE"] = "Static Foo";
170 | }
171 | }
172 |
173 | // -------------------------
174 |
175 | [Fact]
176 | public void CallOverloadedPrivateInstanceMethod_of_DerivedClass_by_LateBinder()
177 | {
178 | object obj = new DerivedTestTargetClass();
179 |
180 | obj.ToLateBind().Call("MethodC", "Adelie")
181 | .IsInstanceOf()
182 | .Is("Method-C by string: Adelie");
183 |
184 | obj.ToLateBind().Call("MethodC", 27)
185 | .IsInstanceOf()
186 | .Is(27);
187 | }
188 |
189 | [Fact]
190 | public void GetAndSetPrivateInstanceProperty_of_DerivedClass_by_LateBinder()
191 | {
192 | object obj = new DerivedTestTargetClass();
193 | obj.ToLateBind().Prop["PropA"]
194 | .IsInstanceOf()
195 | .Is("Fizz");
196 |
197 | obj.ToLateBind().Prop["PropA"] = "Buzz";
198 |
199 | obj.ToLateBind().Prop["PropA"]
200 | .Is("Buzz");
201 | }
202 |
203 | [Fact]
204 | public void GetAndSetPrivateInstanceField_of_DerivedClass_by_LateBinder()
205 | {
206 | object obj = new DerivedTestTargetClass();
207 | obj.ToLateBind().Field["_FieldB"]
208 | .IsInstanceOf()
209 | .Is(DateTime.Parse("2014/02/13 14:27:56"));
210 |
211 | obj.ToLateBind().Field["_FieldB"] = DateTime.Parse("2015/11/09 02:06:03");
212 |
213 | obj.ToLateBind().Field["_FieldB"]
214 | .Is(DateTime.Parse("2015/11/09 02:06:03"));
215 | }
216 |
217 | [Fact]
218 | public void CallOverloadedPrivateStaticMethod_of_DerivedClass_by_LateBinder()
219 | {
220 | var binder = LateBinder.Create(typeof(DerivedTestTargetClass));
221 |
222 | binder.Call("MethodF", "Gentoo", 18)
223 | .IsInstanceOf()
224 | .Is("Method-F(int): Gentoo / 18");
225 |
226 | binder.Call("MethodF", "RockHoper", 31.4)
227 | .IsInstanceOf()
228 | .Is("Method-F(double): RockHoper / 31.4");
229 | }
230 |
231 | [Fact]
232 | public void GetAndSetPrivateStaticProperty_of_DerivedClass_by_LateBinder()
233 | {
234 | var binder = LateBinder.Create();
235 | binder.Prop["PropD"]
236 | .IsNull();
237 |
238 | try
239 | {
240 | binder.Prop["PropD"] = "FizzBuzz";
241 |
242 | binder.Prop["PropD"]
243 | .IsInstanceOf()
244 | .Is("FizzBuzz");
245 | }
246 | finally
247 | {
248 | binder.Prop["PropD"] = null;
249 | }
250 | }
251 |
252 | [Fact]
253 | public void GetAndSetPrivateStaticField_of_DerivedClass_by_LateBinder()
254 | {
255 | object obj = new DerivedTestTargetClass();
256 | var binder = LateBinder.Create(obj.GetType());
257 |
258 | binder.Field["_FieldE"]
259 | .IsInstanceOf()
260 | .Is("Static Foo");
261 |
262 | try
263 | {
264 | binder.Field["_FieldE"] = "Static Bar";
265 |
266 | binder.Field["_FieldE"]
267 | .Is("Static Bar");
268 | }
269 | finally
270 | {
271 | binder.Field["_FieldE"] = "Static Foo";
272 | }
273 | }
274 |
275 | [Fact]
276 | public void CallOverloadedPrivateInstanceMethod_by_LateBinder_with_Cache()
277 | {
278 | object obj = new TestTargetClass();
279 | var cache = new Dictionary>();
280 |
281 | obj.ToLateBind(cache).Call("MethodC", "Adelie")
282 | .IsInstanceOf()
283 | .Is("Method-C by string: Adelie");
284 |
285 | obj.ToLateBind(cache).Call("MethodC", 27)
286 | .IsInstanceOf()
287 | .Is(27);
288 |
289 | obj.ToLateBind(cache).Call("MethodC", "Emperor")
290 | .IsInstanceOf()
291 | .Is("Method-C by string: Emperor");
292 | }
293 |
294 | [Fact]
295 | public void CallPrivateInstanceMethod_with_ref_and_out_Argument_by_LateBinder()
296 | {
297 | object obj = new TestTargetClass();
298 |
299 | var args = new object[] { 3, 4, default(int) };
300 | obj.ToLateBind().Call("MethodG", args);
301 |
302 | args[1].Is(5);
303 | args[2].Is(12);
304 | }
305 |
306 | [Fact]
307 | public void CallPrivateStaticMethod_with_ref_and_out_Argument_by_LateBinder()
308 | {
309 | var binder = LateBinder.Create();
310 |
311 | var args = new object[] { 3, 4, default(int) };
312 | binder.Call("MethodH", args);
313 |
314 | args[1].Is(10);
315 | args[2].Is(6);
316 | }
317 |
318 | [Fact]
319 | public void RetrieveObject_and_UseIt_by_LateBinder()
320 | {
321 | // Given
322 | var binder = LateBinder.Create();
323 | var subItem = binder.Call("GetSubItem").ToLateBind();
324 |
325 | // When
326 | var name = binder.Call("GetNameOfSubItem", subItem) as string;
327 |
328 | // Then
329 | name.Is("John");
330 | ((int)subItem.Prop["Value"]).Is(40);
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/DynamicBinder.Test/TestTargetClass.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable 414 // disable warning CS0414: The field '*' is assigned but its value is never used.
2 |
3 | namespace DynamicBinderTest;
4 |
5 | public class TestTargetClass
6 | {
7 | public class SubItemClass1
8 | {
9 | public string Name { get; set; }
10 | private int Value { get; set; }
11 | public SubItemClass1(string name, int value) { this.Name = name; this.Value = value; }
12 | public SubItemClass1(SubItemClass1 subItem) { this.Name = subItem.Name; this.Value = subItem.Value; }
13 | public override string ToString() { return "Good Job!"; }
14 | }
15 |
16 | private class SubItemClass2
17 | {
18 | public string Name { get; set; }
19 | private int Value { get; set; }
20 | public SubItemClass2(string name, int value) { this.Name = name; this.Value = value; }
21 | }
22 |
23 | // Instance members
24 | // ==============
25 |
26 | private string PropA { get; set; }
27 |
28 | private DateTime _FieldB = DateTime.Parse("2014/02/13 14:27:56");
29 |
30 | private SubItemClass1 PropG { get; set; }
31 |
32 | private SubItemClass2 PropH { get; set; }
33 |
34 | ///
35 | /// Public Constractor
36 | ///
37 | public TestTargetClass()
38 | {
39 | this.PropA = "Fizz";
40 | this.PropG = new SubItemClass1("Sam", 33);
41 | this.PropH = new SubItemClass2("Alice", 29);
42 | }
43 |
44 | ///
45 | /// Private Constractor 1
46 | ///
47 | private TestTargetClass(string propA) : this()
48 | {
49 | this.PropA = propA;
50 | }
51 |
52 | ///
53 | /// Private Constractor 2
54 | ///
55 | private TestTargetClass(string propA, DateTime fieldB) : this(propA)
56 | {
57 | this._FieldB = fieldB;
58 | }
59 |
60 | private string MethodC(string name)
61 | {
62 | return "Method-C by string: " + name;
63 | }
64 |
65 | private int MethodC(int age)
66 | {
67 | return age;
68 | }
69 |
70 | private void MethodG(int x, ref int y, out int z)
71 | {
72 | z = x * y;
73 | y = y + 1;
74 | }
75 |
76 | public SubItemClass1 CreateSubItem1()
77 | {
78 | return new SubItemClass1("Jude", 47);
79 | }
80 |
81 | // Static members
82 | // ==============
83 |
84 | private static string PropD { get; set; }
85 |
86 | private static string _FieldE = "Static Foo";
87 |
88 | private static string MethodF(string name, int age)
89 | {
90 | return "Method-F(int): " + name + " / " + age.ToString();
91 | }
92 |
93 | private static string MethodF(string name, double age)
94 | {
95 | return "Method-F(double): " + name + " / " + age.ToString();
96 | }
97 |
98 | private static void MethodH(int x, ref int y, out int z)
99 | {
100 | z = x * 2;
101 | y = y + z;
102 | }
103 |
104 | private static SubItemClass2 GetSubItem()
105 | {
106 | return new SubItemClass2("John", 40);
107 | }
108 |
109 | private static string GetNameOfSubItem(SubItemClass2 subItem)
110 | {
111 | return subItem.Name;
112 | }
113 | }
114 |
115 | public class DerivedTestTargetClass : TestTargetClass
116 | {
117 | }
118 |
--------------------------------------------------------------------------------
/DynamicBinder.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32317.152
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicBinder", "DynamicBinder\DynamicBinder.csproj", "{005BD7D1-5601-421F-8AB1-9EBBC2B0128D}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "README", "README", "{96C3190E-5747-41F6-A888-C7C7293FB083}"
9 | ProjectSection(SolutionItems) = preProject
10 | LICENSE = LICENSE
11 | README.md = README.md
12 | RELEASE-NOTES.txt = RELEASE-NOTES.txt
13 | EndProjectSection
14 | EndProject
15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicBinder.Test", "DynamicBinder.Test\DynamicBinder.Test.csproj", "{DA985DF9-0C2F-472F-BD3C-44BDD095436A}"
16 | EndProject
17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Actions", "GitHub Actions", "{F770FA13-066F-4457-A073-3385609B88EF}"
18 | ProjectSection(SolutionItems) = preProject
19 | .github\workflows\unit-tests.yml = .github\workflows\unit-tests.yml
20 | EndProjectSection
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {005BD7D1-5601-421F-8AB1-9EBBC2B0128D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {005BD7D1-5601-421F-8AB1-9EBBC2B0128D}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {005BD7D1-5601-421F-8AB1-9EBBC2B0128D}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {005BD7D1-5601-421F-8AB1-9EBBC2B0128D}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {DA985DF9-0C2F-472F-BD3C-44BDD095436A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {DA985DF9-0C2F-472F-BD3C-44BDD095436A}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {DA985DF9-0C2F-472F-BD3C-44BDD095436A}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {DA985DF9-0C2F-472F-BD3C-44BDD095436A}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {10F0692E-A4EE-4D1E-B2A1-CDE0B1CD0DBD}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/DynamicBinder/Binder.cs:
--------------------------------------------------------------------------------
1 | namespace Toolbelt
2 | {
3 | internal static class Binder
4 | {
5 | internal static void UnwrapBinder(object[] args)
6 | {
7 | for (var i = 0; i < args.Length; i++)
8 | {
9 | if (args[i] is DynamicBinder dynamicBinder) args[i] = dynamicBinder.Object;
10 | else if (args[i] is LateBinder lateBinder) args[i] = lateBinder.Object;
11 | }
12 | }
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/DynamicBinder/DynamicBinder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Dynamic;
3 | using System.Globalization;
4 | using System.Reflection;
5 | using Toolbelt.DynamicBinderExtension;
6 |
7 | namespace Toolbelt
8 | {
9 | public class DynamicBinder : DynamicObject
10 | {
11 | protected LateBinder _Binder;
12 |
13 | internal DynamicBinder(LateBinder accessor)
14 | {
15 | this._Binder = accessor;
16 | }
17 |
18 | public static dynamic CreateInstance(params object[] args)
19 | {
20 | return CreateInstance(typeof(T), args);
21 | }
22 |
23 | public static dynamic CreateInstance(Type type, params object[] args)
24 | {
25 | Binder.UnwrapBinder(args);
26 |
27 | var obj = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, args, default(CultureInfo));
28 | return obj.ToDynamic();
29 | }
30 |
31 | public DynamicBinder(object target)
32 | {
33 | this._Binder = new LateBinder(target);
34 | }
35 |
36 | /// get the object that dynamic binding taret.
37 | public object Object { get { return this._Binder.Object; } }
38 |
39 |
40 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
41 | {
42 | Binder.UnwrapBinder(args);
43 |
44 | if (base.TryInvokeMember(binder, args, out result)) return true;
45 | result = Wrap(this._Binder.Call(binder.Name, args));
46 | return true;
47 | }
48 |
49 | public override bool TryGetMember(GetMemberBinder binder, out object result)
50 | {
51 | if (base.TryGetMember(binder, out result)) return true;
52 | if (this._Binder.Prop.Has(binder.Name))
53 | {
54 | result = Wrap(this._Binder.Prop[binder.Name]);
55 | return true;
56 | }
57 | else if (this._Binder.Field.Has(binder.Name))
58 | {
59 | result = Wrap(this._Binder.Field[binder.Name]);
60 | return true;
61 | }
62 | return false;
63 | }
64 |
65 | public override bool TrySetMember(SetMemberBinder binder, object value)
66 | {
67 | if (base.TrySetMember(binder, value)) return true;
68 | if (this._Binder.Prop.Has(binder.Name))
69 | {
70 | this._Binder.Prop[binder.Name] = value;
71 | return true;
72 | }
73 | else if (this._Binder.Field.Has(binder.Name))
74 | {
75 | this._Binder.Field[binder.Name] = value;
76 | return true;
77 | }
78 | return false;
79 | }
80 |
81 | public override bool TryConvert(ConvertBinder binder, out object result)
82 | {
83 | if (base.TryConvert(binder, out result)) return true;
84 | result = this._Binder.Object;
85 | return true;
86 | }
87 |
88 | private static object Wrap(object obj)
89 | {
90 | return obj == null ? null :
91 | Type.GetTypeCode(obj.GetType()) == TypeCode.Object ?
92 | new DynamicBinder(obj) : obj;
93 | }
94 |
95 | public static dynamic Create()
96 | {
97 | return new DynamicBinder(LateBinder.Create());
98 | }
99 |
100 | public static dynamic Create(Type type)
101 | {
102 | return new DynamicBinder(LateBinder.Create(type));
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/DynamicBinder/DynamicBinder.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | 2.2.0
7 | DynamicBinder and LateBinder
8 | J.Sakamoto
9 | J.Sakamoto
10 | Copyright © J.Sakamoto 2014-2023
11 | This library allows you dynamic access to object methods, properties, and fields by using the reflection technology of .NET, regardless of whether they are private members. You can access both object instance members and class static members by name that specified string argument at runtime, not compile-time, or C# 4.0 "dynamic" syntax.
12 |
13 | (Please write the package release notes in "../RELEASE-NOTES.txt")
14 | LGPL-3.0-or-later
15 | https://github.com/jsakamoto/dynamicbinder
16 | reflection,dynamic,late binding
17 | README.md
18 | nupkg-icon.png
19 | ../_dist
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | @(ReleaseNoteLines, '%0a')
33 | $([System.Text.RegularExpressions.Regex]::Match($(PackageReleaseNotes), "^(v\.[\d\.]+.+?)v\.[\d\.]+", System.Text.RegularExpressions.RegexOptions.Singleline).Groups[1].Value)
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/DynamicBinder/DynamicBinderExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 |
5 | namespace Toolbelt.DynamicBinderExtension
6 | {
7 | public static class DynamicBinderExtension
8 | {
9 | public static LateBinder ToLateBind(this object self)
10 | {
11 | return new LateBinder(self);
12 | }
13 |
14 | public static LateBinder ToLateBind(this object self, IDictionary> cache)
15 | {
16 | return new LateBinder(self).SetCache(cache);
17 | }
18 |
19 | public static dynamic ToDynamic(this object self)
20 | {
21 | return new DynamicBinder(new LateBinder(self));
22 | }
23 |
24 | public static dynamic ToDynamic(this object self, IDictionary> cache)
25 | {
26 | return new DynamicBinder(new LateBinder(self).SetCache(cache));
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/DynamicBinder/LateBinder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Reflection;
6 | using Toolbelt.DynamicBinderExtension;
7 |
8 | namespace Toolbelt
9 | {
10 | public class LateBinder
11 | {
12 | protected object _Target;
13 |
14 | protected Type _TypeOfTarget;
15 |
16 | protected BindingFlags _BindingFlags;
17 |
18 | public static LateBinder CreateInstance(params object[] args)
19 | {
20 | return CreateInstance(typeof(T), args);
21 | }
22 |
23 | public static LateBinder CreateInstance(Type type, params object[] args)
24 | {
25 | Binder.UnwrapBinder(args);
26 | var obj = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, args, default(CultureInfo));
27 | return obj.ToLateBind();
28 | }
29 |
30 | protected IDictionary> _Cache;
31 |
32 | public PropertyBinder Prop { get; protected set; }
33 |
34 | public FieldBinder Field { get; protected set; }
35 |
36 | public LateBinder(object target)
37 | {
38 | this.Initi(target, target.GetType());
39 | }
40 |
41 | protected LateBinder(object target, Type typeOfTarget)
42 | {
43 | this.Initi(target, typeOfTarget);
44 | }
45 |
46 | private void Initi(object target, Type typeOfTarget)
47 | {
48 | this._Target = target;
49 | this._TypeOfTarget = typeOfTarget;
50 | this._BindingFlags = BindingFlags.Public | BindingFlags.NonPublic | (target != null ? BindingFlags.Instance : BindingFlags.Static);
51 | this.Prop = new PropertyBinder(this);
52 | this.Field = new FieldBinder(this);
53 | }
54 |
55 | public static LateBinder Create(Type type)
56 | {
57 | return new LateBinder(null, type);
58 | }
59 |
60 | public static LateBinder Create()
61 | {
62 | return new LateBinder(null, typeof(T));
63 | }
64 |
65 | public LateBinder SetCache(IDictionary> cache)
66 | {
67 | this._Cache = cache;
68 | return this;
69 | }
70 |
71 | /// get the object that late binding taret.
72 | public object Object { get { return this._Target; } }
73 |
74 | public object Call(string methodName, params object[] args)
75 | {
76 | Binder.UnwrapBinder(args);
77 | var argTypes = args.Select(_ => _ != null ? _.GetType() : typeof(object)).ToArray();
78 | var memberSufix = "(" + string.Join(",", argTypes.Select(t => t.FullName)) + ")";
79 | var methodInfo = this.FindMember(
80 | methodName,
81 | finder: t =>
82 | {
83 | var method = t.GetMethod(methodName, this._BindingFlags, null, argTypes, null);
84 | if (method != null) return method;
85 | var methods = t.GetMethods(this._BindingFlags).Where(m => m.Name == methodName).ToArray();
86 | if (methods.Length == 1) return methods.First();
87 | return methods.Where(m =>
88 | {
89 | var parameters = m.GetParameters();
90 | return parameters
91 | .Select((p, index) => p.ParameterType.FullName.TrimEnd('&') == argTypes[index].FullName)
92 | .All(_ => _);
93 | }).FirstOrDefault();
94 | },
95 | memberSufix: memberSufix);
96 | return methodInfo.Invoke(this._Target, args);
97 | }
98 |
99 | protected static IEnumerable EnumType(Type type)
100 | {
101 | for (; type != null; type = type.BaseType)
102 | {
103 | yield return type;
104 | }
105 | }
106 |
107 | protected IDictionary GetCacheOfMe()
108 | {
109 | if (this._Cache == null) return null;
110 | var cacheOfMe = default(IDictionary);
111 | lock (this._Cache)
112 | {
113 | if (this._Cache.TryGetValue(this._TypeOfTarget, out cacheOfMe) == false)
114 | {
115 | cacheOfMe = new Dictionary();
116 | this._Cache.Add(this._TypeOfTarget, cacheOfMe);
117 | }
118 | }
119 | return cacheOfMe;
120 | }
121 |
122 | internal T FindMember(
123 | string memberName,
124 | Func finder,
125 | bool throwExceptionIfMemberNotFound = true,
126 | string memberSufix = ""
127 | ) where T : MemberInfo
128 | {
129 | Func findMember = () =>
130 | {
131 | var memberInfo = EnumType(this._TypeOfTarget)
132 | .Select(t => finder(t))
133 | .FirstOrDefault(m => m != null);
134 | if (memberInfo == null && throwExceptionIfMemberNotFound)
135 | throw new Exception("Member " + memberName + memberSufix + " not found.");
136 | return memberInfo;
137 | };
138 |
139 | var cacheOfMe = this.GetCacheOfMe();
140 | if (cacheOfMe == null)
141 | {
142 | return findMember();
143 | }
144 | else
145 | {
146 | lock (cacheOfMe)
147 | {
148 | var memberInfo = default(MemberInfo);
149 | if (cacheOfMe.TryGetValue(memberName + memberSufix, out memberInfo) == false)
150 | {
151 | memberInfo = findMember();
152 | cacheOfMe.Add(memberName + memberSufix, memberInfo);
153 | }
154 | return (T)memberInfo;
155 | }
156 | }
157 | }
158 |
159 | public class PropertyBinder
160 | {
161 | protected LateBinder _Binder;
162 |
163 | internal PropertyBinder(LateBinder accessor)
164 | {
165 | this._Binder = accessor;
166 | }
167 |
168 | public PropertyInfo GetInfo(string propName, bool throwExceptionIfMemberNotFound = true)
169 | {
170 | return this._Binder.FindMember(propName, t => t.GetProperty(propName, this._Binder._BindingFlags), throwExceptionIfMemberNotFound);
171 | }
172 |
173 | public object this[string propName]
174 | {
175 | get
176 | {
177 | var propInfo = this.GetInfo(propName);
178 | return propInfo.GetValue(this._Binder._Target, null);
179 | }
180 | set
181 | {
182 | var propInfo = this.GetInfo(propName);
183 | propInfo.SetValue(this._Binder._Target, value, null);
184 | }
185 | }
186 |
187 | public bool Has(string propName)
188 | {
189 | return this.GetInfo(propName, false) != null;
190 | }
191 | }
192 |
193 | public class FieldBinder
194 | {
195 | protected LateBinder _Binder;
196 |
197 | internal FieldBinder(LateBinder accessor)
198 | {
199 | this._Binder = accessor;
200 | }
201 |
202 | public FieldInfo GetInfo(string fieldName, bool throwExceptionIfMemberNotFound = true)
203 | {
204 | return this._Binder.FindMember(fieldName, t => t.GetField(fieldName, this._Binder._BindingFlags), throwExceptionIfMemberNotFound);
205 | }
206 |
207 | public object this[string fieldName]
208 | {
209 | get
210 | {
211 | var fieldInfo = this.GetInfo(fieldName);
212 | return fieldInfo.GetValue(this._Binder._Target);
213 | }
214 | set
215 | {
216 | var fieldInfo = this.GetInfo(fieldName);
217 | fieldInfo.SetValue(this._Binder._Target, value);
218 | }
219 | }
220 |
221 | public bool Has(string fieldName)
222 | {
223 | return this.GetInfo(fieldName, false) != null;
224 | }
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DynamicBinder and LateBinder [](https://www.nuget.org/packages/DynamicBinder/) [](https://github.com/jsakamoto/dynamicbinder/actions/workflows/unit-tests.yml)
2 |
3 | ## What's this?
4 |
5 | This is the class library for .NET.
6 |
7 | This library allows you dynamic access to object methods, properties, and fields using the reflection technology of .NET, regardless of whether they are private members.
8 |
9 | You can access both objects' instance members and classes' static members by name that specified string argument at runtime, not compile-time, or C# 4.0 "dynamic" syntax.
10 |
11 | ## How to install?
12 |
13 | You can install this library via [NuGet](https://www.nuget.org/packages/DynamicBinder/).
14 |
15 | ```shell
16 | > dotnet add package DynamicBinder
17 | ```
18 |
19 | ## Usage - C# "dynamic" syntax
20 |
21 | ### Create instance
22 |
23 | After importing (opening) namespace `Toolbelt`, you can use `DynamicBinder.CreateInstance(...)` and `DynamicBinder.CreateInstance(Type t, ...)` static method to instantiate any objects with any constructors, regardless of constructor's access level (public, internal, protected, private).
24 |
25 | Those methods return an instantiated object wrapped with the `DynamicBinder` object as a `dynamic` type.
26 |
27 | ```csharp
28 | using Toolbelt;
29 | ...
30 | // 👇 The type of the "dynamicObj" is the "dynamic" type.
31 | // In this case, the "dynamicObj" is instantiated by the constructor, which has two arguments.
32 | // It can be instantiated even if the constructor is private.
33 | var dynamicObj = DynamicBinder.CreateInstance(arg1, arg2);
34 |
35 | // And it can be invoked its instance methods, regardless of its access level.
36 | var retval = (int)dynamicObj.PrivateMethodName(arg3, arg4);
37 | ...
38 | ```
39 |
40 | ### Access to instance members
41 |
42 | After importing (opening) namespace `Toolbelt.DynamicBinderExtension`, you can use `ToDynamic()` extension method that returned C #4.0 `dynamic` type at any object.
43 |
44 | ```csharp
45 | using Toolbelt.DynamicBinderExtension;
46 | ...
47 | var obj = new MyClass();
48 | ...
49 | // Call an instance method.
50 | // (You can pass ref & out arguments.)
51 | var retval = (int)obj.ToDynamic().MethodName(arg1, ref arg2, out int arg3);
52 |
53 | // Get or set an instance property.
54 | var value = (int)obj.ToDynamic().PropName;
55 | obj.ToDynamic().PropName = newValue;
56 |
57 | // Get or set an instance field.
58 | var value = (int)obj.ToDynamic().FieldName;
59 | obj.ToDynamic().FieldName = newValue;
60 | ```
61 |
62 | ### Access to static members
63 |
64 | After importing (opening) namespace `Toolbelt`, you can use `DynamicBinder.Create()` and `DynamicBinder.Create(Type t)` static method that returned C #4.0 `dynamic` type.
65 |
66 | ```csharp
67 | using Toolbelt;
68 | ...
69 | var binder = DynamicBinder.Create(typeof(Foo));
70 |
71 | // Call a static method.
72 | // (You can pass ref & out arguments.)
73 | var retval = (int)binder.MethodName(arg1, ref arg2, out int arg3);
74 |
75 | // Get or set a static property.
76 | var value = (int)binder.PropName;
77 | binder.PropName = newValue;
78 |
79 | // Get or set a static field.
80 | var value = (int)binder.FieldName;
81 | binder.FieldName = newValue;
82 | ```
83 |
84 | ### NOTICE: Retrieving a type information of the returned value from the method calling
85 |
86 | Retrieving a type information of the return value from the method calling
87 |
88 | The following test code will fail.
89 |
90 | ```csharp
91 | object bar = foo.ToDynamic().GetBarObject();
92 |
93 | // 👇 It will report "actual is `DynamicBinder`" !
94 | Assert.AreEqual("BarClass", bar.GetType().Name);
95 | ```
96 |
97 | You should rewrite the above test code as follows.
98 |
99 | ```csharp
100 | // Extract a `DynamicBinder` object from the C# dynamic object by casting with `as`.
101 | var retval = foo.ToDynamic().GetBarObject() as DynamicBinder;
102 |
103 | // The `DynamicBinder` class exposes the `Object` property to access the bound target object.
104 | Assert.AreEqual("BarClass", retval.Object.GetType().Name); // Green. Pass!
105 | ```
106 |
107 | Of course, if you have the right type information, those test codes can be rewritten as the following:
108 |
109 | ```csharp
110 | var bar = (BarClass)foo.ToDynamic().GetBarObject();
111 | Assert.AreEqual("BarClass", bar.GetType().Name); // Green. Pass!
112 | ```
113 |
114 |
115 | ## Usage - Late bind syntax
116 |
117 | ### Create instance
118 |
119 | After importing (opening) namespace `Toolbelt`, you can use `LateBinder.CreateInstance(...)` and `LateBinder.CreateInstance(Type t, ...)` static method to instantiate any objects with any constructors, regardless of constructor's access level (public, internal, protected, private).
120 |
121 | Those methods return an instantiated object wrapped with the `LateBinder` type.
122 |
123 | ```csharp
124 | using Toolbelt;
125 | ...
126 | // 👇 The type of the "dynamicObj" is the "LateBinder" type.
127 | // In this case, the "dynamicObj" is instantiated by the constructor, which has two arguments.
128 | // It can be instantiated even if the constructor is private.
129 | var dynamicObj = LateBinder.CreateInstance(arg1, arg2);
130 |
131 | // And it can be invoked its instance methods, regardless of its access level.
132 | var retval = (int)dynamicObj.Call("PrivateMethodName", arg3, arg4);
133 | ...
134 | ```
135 |
136 | ### Access to instance members
137 |
138 | After importing (opening) namespace `Toolbelt.DynamicBinderExtension`, you can use `ToLateBind()` extension method that returned `LateBinder` object at any object.
139 |
140 | `LateBinder` has follow members.
141 |
142 | - `Call(name, params[] args)` method
143 | - `Prop[name]` property
144 | - `Field[name]` property
145 |
146 | ```csharp
147 | using Toolbelt.DynamicBinderExtension;
148 | ...
149 | // Call an instance method.
150 | var retval = (int)obj.ToLateBind().Call("MethodName", arg1, arg2);
151 |
152 | // Get or set an instance property.
153 | var value = (int)obj.ToLateBind().Prop["PropName"];
154 | obj.ToLateBind().Prop["PropName"] = newValue;
155 |
156 | // Get or set an instance field.
157 | var value = (int)obj.ToLateBind().Field["FieldName"];
158 | obj.ToLateBind().Field["FieldName"] = newValue;
159 | ```
160 |
161 | ### Access to static members
162 |
163 | After importing (opening) namespace `Toolbelt`, you can use `LateBinder.Create()` and `LateBinder.Create(Type t)` static method that returned `LateBinder` object.
164 |
165 | ```csharp
166 | using Toolbelt;
167 | ...
168 | var binder = LateBinder.Create();
169 | // Call a static method.
170 | var retval = (int)binder.Call("MethodName", arg1, arg2);
171 |
172 | // Get or set a static property.
173 | var value = (int)binder.Prop["PropName"];
174 | binder.Prop["PropName"] = newValue;
175 |
176 | // Get or set a static field.
177 | var value = (int)binder.Field["FieldName"];
178 | binder.Field["FieldName"] = newValue;
179 | ```
180 |
181 | ### Call methods with `ref` & `out` arguments
182 |
183 | To call methods with `ref` & `out` arguments, you can't pass those arguments to the `Call` method of the late-binder directly, which is different from using the "dynamic" syntax. Instead, follow the instructions below.
184 |
185 | ```csharp
186 | // For example, if the definition of the "MethodName" static method is:
187 |
188 | // static void MethodName(int x, ref int y, out int z){
189 | // z = x * y;
190 | // y = y + 1;
191 | // }
192 |
193 | // 1. Pack all of the arguments into an object array.
194 | var args = new object[] { 3, 4, default(int) };
195 |
196 | // 2. Pass it to the 2nd argument of the "Call" method.
197 | binder.Call("MethodName", args);
198 |
199 | // 3. Then, the "args" array will be updated.
200 | // args[1] -> 5
201 | // args[2] -> 12
202 | ```
203 |
204 | ## Note
205 |
206 | ### No using extension methods scenario
207 |
208 | If you feel these extension methods are dirty, you can choose no using these extension methods.
209 |
210 | Instead, you can use `LateBinder` and `DynamicBinder` class like the following code.
211 |
212 | ```csharp
213 | // Do not open namespace "Toolbelt.DynamicBinderExtension".
214 | using Toolbelt;
215 | ...
216 | // Instead, instantiate DynamicBinder or LateBinder objects with the "new" keyword.
217 | dynamic dynamicBinder = new DynamicBinder(obj);
218 | var retval = (int)dynamicBinder.MethodName(arg1, arg2);
219 |
220 | var lateBinder = new LateBinder(obj);
221 | var retval = (int)lateBinder.Call("MethodName", arg1, arg2);
222 | ```
223 |
224 | ### "Reinventing the wheel"
225 |
226 | There are no less than 50 packages about reflection & private members accessing.
227 |
228 | - 🔍 https://www.nuget.org/packages?q=Tags%3A%22reflection%22
229 |
230 | But I couldn't find any packages with my favorite syntax and features :).
231 |
232 | So I decided to "reinvent the wheel" by my hand.
233 |
234 |
235 | ### Performance issue
236 |
237 | In this library, `DynamicBinder` and `LateBinder` may be much slower because their implementation uses the reflection API directory without any technics such as caches, compiling to delegations, compiling to expressions, etc.
238 |
239 | Therefore, I think there is plenty of room for improvement to faster, more high performance.
240 |
241 | If you prefer, you can fork this repository and improve it.
242 |
243 | ## Release Notes
244 |
245 | Release notes are [here.](https://github.com/jsakamoto/dynamicbinder/blob/master/RELEASE-NOTES.txt)
246 |
247 | ## Licence
248 |
249 | - [GNU Lesser General Public License v3.0 or later](https://github.com/jsakamoto/dynamicbinder/blob/master/LICENSE)
250 |
--------------------------------------------------------------------------------
/RELEASE-NOTES.txt:
--------------------------------------------------------------------------------
1 | v.2.2.0
2 | - Enhance: Allow dynamic method call return values to be used as parameters in a method.
3 |
4 | v.2.1.0
5 | - Enhance: Add support for calling methods with "ref" and "out" arguments.
6 |
7 | v.2.0.0
8 | - Enhance: Add DynamicBinder.CreateInstance() and LateBinder.CreateInstance().
9 | - Drop support for old .NET Framework versions.
10 |
11 | v.1.5.1
12 | - Enhance: Add .NET Standard 2.0 support.
13 |
14 | v.1.5
15 | - Enhance: Add "Object" property which expose the object that binding target.
16 | - Fix bug: Can not extract class type object by DynamicBinder.
17 |
18 | v.1.4
19 | - Fix bug: Can not retrieve class type properties by DynamicBinder.
20 |
21 | v.1.3
22 | - Add avility of exposing nested private object graph.
23 |
24 | v.1.2
25 | - Add avility of caching System.Reflection.MemberInfo (use SetCache(disctionary) method)
26 | - Add GetInfo(name) method on PropertyBinder and FieldBinder that returned System.Reflection.MemberInfo.
27 |
28 | v.1.1
29 | - Support base class members access.
--------------------------------------------------------------------------------
/_dist/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsakamoto/dynamicbinder/88208c45e2e5013dec3c8417c2828118338ff813/_dist/.gitkeep
--------------------------------------------------------------------------------