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