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 }