Mercurial > pub > bltoolkit
comparison Source/Patterns/DuckTyping.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 | |
5 using BLToolkit.Common; | |
6 using BLToolkit.Properties; | |
7 using BLToolkit.Reflection; | |
8 using BLToolkit.TypeBuilder; | |
9 using BLToolkit.TypeBuilder.Builders; | |
10 | |
11 namespace BLToolkit.Patterns | |
12 { | |
13 /// <summary> | |
14 /// Duck typing implementation. | |
15 /// In computer science, duck typing is a term for dynamic typing typical | |
16 /// of some programming languages, such as Smalltalk, Python or ColdFusion, | |
17 /// where a variable's value itself determines what the variable can do. | |
18 /// Thus an object or set of objects having all the methods described in | |
19 /// an interface can be made to implement that interface dynamically | |
20 /// at runtime, even if the objects class does not include the interface | |
21 /// in its implements clause. | |
22 /// </summary> | |
23 public static class DuckTyping | |
24 { | |
25 #region Single Duck | |
26 | |
27 static readonly Dictionary<Type,Dictionary<object,Type>> _duckTypes = new Dictionary<Type,Dictionary<object,Type>>(); | |
28 | |
29 /// <summary> | |
30 /// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. | |
31 /// </summary> | |
32 /// <param name="interfaceType">An interface type to implement.</param> | |
33 /// <param name="objectType">Any type which expected to have all members of the given interface.</param> | |
34 /// <returns>The duck object type.</returns> | |
35 public static Type GetDuckType(Type interfaceType, Type objectType) | |
36 { | |
37 if (interfaceType == null) throw new ArgumentNullException("interfaceType"); | |
38 if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); | |
39 if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) | |
40 throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); | |
41 | |
42 Dictionary<object,Type> types; | |
43 | |
44 lock(_duckTypes) | |
45 if (!_duckTypes.TryGetValue(interfaceType, out types)) | |
46 _duckTypes.Add(interfaceType, types = new Dictionary<object,Type>()); | |
47 | |
48 Type type; | |
49 | |
50 lock (types) if (!types.TryGetValue(objectType, out type)) | |
51 { | |
52 type = TypeFactory.GetType( | |
53 new CompoundValue(interfaceType, objectType), | |
54 interfaceType, //objectType, | |
55 new DuckTypeBuilder(MustImplementAttribute.Default, interfaceType, new[] { objectType })); | |
56 | |
57 types.Add(objectType, type); | |
58 } | |
59 | |
60 return type; | |
61 } | |
62 | |
63 /// <summary> | |
64 /// Implements the requested interface for supplied object. | |
65 /// If the supplied object implements the interface, the object itself will be returned. | |
66 /// Otherwise a convenient duck object will be created. | |
67 /// </summary> | |
68 /// <param name="interfaceType">An interface type to implement.</param> | |
69 /// <param name="baseObjectType">Any type which has all members of the given interface. | |
70 /// When this parameter is set to null, the object type will be used.</param> | |
71 /// <param name="obj">An object which type expected to have all members of the given interface.</param> | |
72 /// <returns>An object which implements the interface.</returns> | |
73 public static object Implement(Type interfaceType, Type baseObjectType, object obj) | |
74 { | |
75 if (obj == null) throw new ArgumentNullException("obj"); | |
76 | |
77 var objType = obj.GetType(); | |
78 | |
79 if (TypeHelper.IsSameOrParent(interfaceType, objType)) | |
80 return obj; | |
81 | |
82 if (obj is DuckType) | |
83 { | |
84 var duckObject = (DuckType)obj; | |
85 | |
86 if (duckObject.Objects.Length == 1) | |
87 { | |
88 // Switch to underlying objects when a duck object was passed. | |
89 // | |
90 return Implement(interfaceType, baseObjectType, duckObject.Objects[0]); | |
91 } | |
92 | |
93 // Re-aggregate underlying objects to expose new interface. | |
94 // | |
95 return Aggregate(interfaceType, duckObject.Objects); | |
96 } | |
97 | |
98 if (baseObjectType == null) | |
99 baseObjectType = objType; | |
100 else if (!TypeHelper.IsSameOrParent(baseObjectType, objType)) | |
101 throw new ArgumentException(string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectType.FullName), "obj"); | |
102 | |
103 var duckType = GetDuckType(interfaceType, baseObjectType); | |
104 | |
105 if (duckType == null) | |
106 return null; | |
107 | |
108 var duck = TypeAccessor.CreateInstanceEx(duckType); | |
109 | |
110 ((DuckType)duck).SetObjects(obj); | |
111 | |
112 return duck; | |
113 } | |
114 | |
115 /// <summary> | |
116 /// Implements the requested interface. | |
117 /// If the supplied object implements the interface, the object itself will be returned. | |
118 /// Otherwise a convenient duck object will be created. | |
119 /// </summary> | |
120 /// <param name="interfaceType">An interface type to implement.</param> | |
121 /// <param name="obj">An object which type expected to have all members of the given interface.</param> | |
122 /// <returns>An object which implements the interface.</returns> | |
123 public static object Implement(Type interfaceType, object obj) | |
124 { | |
125 return Implement(interfaceType, null, obj); | |
126 } | |
127 | |
128 /// <summary> | |
129 /// Implements the requested interface for all supplied objects. | |
130 /// If any of supplied object implements the interface, the object itself will be returned. | |
131 /// Otherwise a convenient duck object will be created. | |
132 /// </summary> | |
133 /// <param name="interfaceType">An interface type to implement.</param> | |
134 /// <param name="baseObjectType">Any type which has all members of the given interface. | |
135 /// When this parameter is set to null, the object type will be used.</param> | |
136 /// <param name="objects">An object array which types expected to have all members of the given interface. | |
137 /// All objects may have different types.</param> | |
138 /// <returns>An array of object which implements the interface.</returns> | |
139 public static object[] Implement(Type interfaceType, Type baseObjectType, params object[] objects) | |
140 { | |
141 if (objects == null) throw new ArgumentNullException("objects"); | |
142 | |
143 object[] result = new object[objects.Length]; | |
144 | |
145 for (int i = 0; i < objects.Length; i++) | |
146 result[i] = Implement(interfaceType, baseObjectType, objects[i]); | |
147 | |
148 return result; | |
149 } | |
150 | |
151 /// <summary> | |
152 /// Implements the requested interface for all supplied objects. | |
153 /// If any of supplied object implements the interface, the object itself will be returned. | |
154 /// Otherwise a convenient duck object will be created. | |
155 /// </summary> | |
156 /// <param name="interfaceType">An interface type to implement.</param> | |
157 /// <param name="objects">An object array which types expected to have all members of the given interface. | |
158 /// All objects may have different types.</param> | |
159 /// <returns>An array of object which implements the interface.</returns> | |
160 public static object[] Implement(Type interfaceType, params object[] objects) | |
161 { | |
162 return Implement(interfaceType, (Type)null, objects); | |
163 } | |
164 | |
165 /// <summary> | |
166 /// Implements the requested interface for supplied object. | |
167 /// If the supplied object implements the interface, the object itself will be returned. | |
168 /// Otherwise a convenient duck object will be created. | |
169 /// </summary> | |
170 /// <typeparam name="I">An interface type to implement.</typeparam> | |
171 /// <param name="obj">An object which type expected to have all members of the given interface.</param> | |
172 /// <returns>An object which implements the interface.</returns> | |
173 public static I Implement<I>(object obj) | |
174 where I : class | |
175 { | |
176 return (I)Implement(typeof(I), null, obj); | |
177 } | |
178 | |
179 /// <summary> | |
180 /// Implements the requested interface for supplied object. | |
181 /// If the supplied object implements the interface, the object itself will be returned. | |
182 /// Otherwise a convenient duck object will be created. | |
183 /// </summary> | |
184 /// <typeparam name="I">An interface type to implement.</typeparam> | |
185 /// <typeparam name="T">Any type which has all members of the given interface.</typeparam> | |
186 /// <param name="obj">An object which type expected to have all members of the given interface.</param> | |
187 /// <returns>An object which implements the interface.</returns> | |
188 public static I Implement<I,T>(T obj) | |
189 where I : class | |
190 { | |
191 return (I)Implement(typeof(I), typeof(T), obj); | |
192 } | |
193 | |
194 /// <summary> | |
195 /// Implements the requested interface for all supplied objects. | |
196 /// If any of supplied object implements the interface, the object itself will be returned. | |
197 /// Otherwise a convenient duck object will be created. | |
198 /// </summary> | |
199 /// <typeparam name="I">An interface type to implement.</typeparam> | |
200 /// <param name="objects">An object array which types expected to have all members of the given interface. | |
201 /// All objects may have different types.</param> | |
202 /// <returns>An array of object which implements the interface.</returns> | |
203 public static I[] Implement<I>(params object[] objects) | |
204 where I : class | |
205 { | |
206 if (objects == null) throw new ArgumentNullException("objects"); | |
207 | |
208 I[] result = new I[objects.Length]; | |
209 | |
210 for (int i = 0; i < objects.Length; i++) | |
211 result[i] = Implement<I>(objects[i]); | |
212 | |
213 return result; | |
214 } | |
215 | |
216 /// <summary> | |
217 /// Implements the requested interface for all supplied objects. | |
218 /// If any of supplied object implements the interface, the object itself will be returned. | |
219 /// Otherwise a convenient duck object will be created. | |
220 /// </summary> | |
221 /// <typeparam name="I">An interface type to implement.</typeparam> | |
222 /// <typeparam name="T">Any type which has all members of the given interface.</typeparam> | |
223 /// <param name="objects">An object array which types expected to have all members of the given interface. | |
224 /// All objects may have different types.</param> | |
225 /// <returns>An array of object which implements the interface.</returns> | |
226 public static I[] Implement<I,T>(params T[] objects) | |
227 where I : class | |
228 { | |
229 if (objects == null) throw new ArgumentNullException("objects"); | |
230 | |
231 I[] result = new I[objects.Length]; | |
232 | |
233 for (int i = 0; i < objects.Length; i++) | |
234 result[i] = Implement<I,T>(objects[i]); | |
235 | |
236 return result; | |
237 } | |
238 | |
239 private static bool _allowStaticMembers; | |
240 public static bool AllowStaticMembers | |
241 { | |
242 get { return _allowStaticMembers; } | |
243 set { _allowStaticMembers = value; } | |
244 } | |
245 | |
246 #endregion | |
247 | |
248 #region Multiple Duck | |
249 | |
250 /// <summary> | |
251 /// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. | |
252 /// </summary> | |
253 /// <param name="interfaceType">An interface type to implement.</param> | |
254 /// <param name="objectTypes">Array of types which expected to have all members of the given interface.</param> | |
255 /// <returns>The duck object type.</returns> | |
256 public static Type GetDuckType(Type interfaceType, Type[] objectTypes) | |
257 { | |
258 if (interfaceType == null) throw new ArgumentNullException("interfaceType"); | |
259 if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); | |
260 if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) | |
261 throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); | |
262 | |
263 Dictionary<object,Type> types; | |
264 | |
265 lock (_duckTypes) | |
266 if (!_duckTypes.TryGetValue(interfaceType, out types)) | |
267 _duckTypes.Add(interfaceType, types = new Dictionary<object,Type>()); | |
268 | |
269 object key = new CompoundValue(objectTypes); | |
270 Type type; | |
271 | |
272 lock (types) if (!types.TryGetValue(key, out type)) | |
273 { | |
274 type = TypeFactory.GetType( | |
275 new CompoundValue(interfaceType, key), | |
276 interfaceType, | |
277 new DuckTypeBuilder(MustImplementAttribute.Aggregate, interfaceType, objectTypes)); | |
278 | |
279 types.Add(key, type); | |
280 } | |
281 | |
282 return type; | |
283 } | |
284 | |
285 /// <summary> | |
286 /// Implements the requested interface from supplied set of objects. | |
287 /// </summary> | |
288 /// <param name="interfaceType">An interface type to implement.</param> | |
289 /// <param name="baseObjectTypes">Array of types which have all members of the given interface. | |
290 /// When this parameter is set to null, the object type will be used.</param> | |
291 /// <param name="objs">Array of objects which types expected to have all members of the given interface.</param> | |
292 /// <returns>An object which implements the interface.</returns> | |
293 public static object Aggregate(Type interfaceType, Type[] baseObjectTypes,params object[] objs) | |
294 { | |
295 if (objs == null) throw new ArgumentNullException("objs"); | |
296 | |
297 if (baseObjectTypes == null) | |
298 { | |
299 baseObjectTypes = new Type[objs.Length]; | |
300 | |
301 for (int i = 0; i < objs.Length; i++) | |
302 if (objs[i] != null) | |
303 baseObjectTypes[i] = objs[i].GetType(); | |
304 } | |
305 else | |
306 { | |
307 if (baseObjectTypes.Length != objs.Length) | |
308 throw new ArgumentException(Resources.DuckTyping_InvalidNumberOfObjs, "baseObjectTypes"); | |
309 | |
310 for (int i = 0; i < objs.Length; i++) | |
311 { | |
312 Type objType = objs[i].GetType(); | |
313 | |
314 if (!TypeHelper.IsSameOrParent(baseObjectTypes[i], objType)) | |
315 throw new ArgumentException( | |
316 string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectTypes[i].FullName), "objs"); | |
317 } | |
318 } | |
319 | |
320 Type duckType = GetDuckType(interfaceType, baseObjectTypes); | |
321 | |
322 if (duckType == null) | |
323 return null; | |
324 | |
325 object duck = TypeAccessor.CreateInstanceEx(duckType); | |
326 | |
327 ((DuckType)duck).SetObjects(objs); | |
328 | |
329 return duck; | |
330 } | |
331 | |
332 /// <summary> | |
333 /// Implements the requested interface from supplied set of objects. | |
334 /// </summary> | |
335 /// <param name="interfaceType">An interface type to implement.</param> | |
336 /// <param name="objs">Array of object which types expected to have of the given interface.</param> | |
337 /// <returns>An object which implements the interface.</returns> | |
338 public static object Aggregate(Type interfaceType,params object[] objs) | |
339 { | |
340 return Aggregate(interfaceType, (Type[])null, objs); | |
341 } | |
342 | |
343 /// <summary> | |
344 /// Implements the requested interface from supplied set of objects. | |
345 /// </summary> | |
346 /// <typeparam name="I">An interface type to implement.</typeparam> | |
347 /// <param name="objs">Array of object which type expected to have all members of the given interface.</param> | |
348 /// <returns>An object which implements the interface.</returns> | |
349 public static I Aggregate<I>(params object[] objs) | |
350 where I : class | |
351 { | |
352 return (I)Aggregate(typeof(I), null, objs); | |
353 } | |
354 | |
355 #endregion | |
356 } | |
357 } |