0
|
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 }
|