using System; using System.Collections; using BLToolkit.Common; using BLToolkit.Properties; using BLToolkit.Reflection; using BLToolkit.TypeBuilder; using BLToolkit.TypeBuilder.Builders; namespace BLToolkit.Patterns { /// /// Duck typing implementation. /// In computer science, duck typing is a term for dynamic typing typical /// of some programming languages, such as Smalltalk, Python or ColdFusion, /// where a variable's value itself determines what the variable can do. /// Thus an object or set of objects having all the methods described in /// an interface can be made to implement that interface dynamically /// at runtime, even if the object’s class does not include the interface /// in its implements clause. /// public static class DuckTyping { #region Single Duck private static readonly Hashtable _duckTypes = new Hashtable(); /// /// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. /// /// An interface type to implement. /// Any type which expected to have all members of the given interface. /// The duck object type. public static Type GetDuckType(Type interfaceType, Type objectType) { if (interfaceType == null) throw new ArgumentNullException("interfaceType"); if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); Hashtable types = (Hashtable)_duckTypes[interfaceType]; if (types == null) { lock (_duckTypes.SyncRoot) { types = (Hashtable)_duckTypes[interfaceType]; if (types == null) _duckTypes.Add(interfaceType, types = new Hashtable()); } } Type type = (Type)types[objectType]; if (type == null) { lock (types.SyncRoot) { type = (Type)types[objectType]; if (type != null || types.ContainsKey(objectType)) return type; type = TypeFactory.GetType( new CompoundValue(interfaceType, objectType), interfaceType, //objectType, new DuckTypeBuilder(MustImplementAttribute.Default, interfaceType, new Type[] { objectType })); types.Add(objectType, type); } } return type; } /// /// Implements the requested interface for supplied object. /// If the supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// Any type which has all members of the given interface. /// When this parameter is set to null, the object type will be used. /// An object which type expected to have all members of the given interface. /// An object which implements the interface. public static object Implement(Type interfaceType, Type baseObjectType, object obj) { if (obj == null) throw new ArgumentNullException("obj"); Type objType = obj.GetType(); if (TypeHelper.IsSameOrParent(interfaceType, objType)) return obj; if (obj is DuckType) { DuckType duckObject = (DuckType)obj; if (duckObject.Objects.Length == 1) { // Switch to underlying objects when a duck object was passed. // return Implement(interfaceType, baseObjectType, duckObject.Objects[0]); } // Re-aggregate underlying objects to expose new interface. // return Aggregate(interfaceType, duckObject.Objects); } if (baseObjectType == null) baseObjectType = objType; else if (!TypeHelper.IsSameOrParent(baseObjectType, objType)) throw new ArgumentException( string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectType.FullName), "obj"); Type duckType = GetDuckType(interfaceType, baseObjectType); if (duckType == null) return null; object duck = TypeAccessor.CreateInstanceEx(duckType); ((DuckType)duck).SetObjects(obj); return duck; } /// /// Implements the requested interface. /// If the supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// An object which type expected to have all members of the given interface. /// An object which implements the interface. public static object Implement(Type interfaceType, object obj) { return Implement(interfaceType, null, obj); } /// /// Implements the requested interface for all supplied objects. /// If any of supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// Any type which has all members of the given interface. /// When this parameter is set to null, the object type will be used. /// An object array which types expected to have all members of the given interface. /// All objects may have different types. /// An array of object which implements the interface. public static object[] Implement(Type interfaceType, Type baseObjectType, params object[] objects) { if (objects == null) throw new ArgumentNullException("objects"); object[] result = new object[objects.Length]; for (int i = 0; i < objects.Length; i++) result[i] = Implement(interfaceType, baseObjectType, objects[i]); return result; } /// /// Implements the requested interface for all supplied objects. /// If any of supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// An object array which types expected to have all members of the given interface. /// All objects may have different types. /// An array of object which implements the interface. public static object[] Implement(Type interfaceType, params object[] objects) { return Implement(interfaceType, (Type)null, objects); } /// /// Implements the requested interface for supplied object. /// If the supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// An object which type expected to have all members of the given interface. /// An object which implements the interface. public static I Implement(object obj) where I : class { return (I)Implement(typeof(I), null, obj); } /// /// Implements the requested interface for supplied object. /// If the supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// Any type which has all members of the given interface. /// An object which type expected to have all members of the given interface. /// An object which implements the interface. public static I Implement(T obj) where I : class { return (I)Implement(typeof(I), typeof(T), obj); } /// /// Implements the requested interface for all supplied objects. /// If any of supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// An object array which types expected to have all members of the given interface. /// All objects may have different types. /// An array of object which implements the interface. public static I[] Implement(params object[] objects) where I : class { if (objects == null) throw new ArgumentNullException("objects"); I[] result = new I[objects.Length]; for (int i = 0; i < objects.Length; i++) result[i] = Implement(objects[i]); return result; } /// /// Implements the requested interface for all supplied objects. /// If any of supplied object implements the interface, the object itself will be returned. /// Otherwise a convenient duck object will be created. /// /// An interface type to implement. /// Any type which has all members of the given interface. /// An object array which types expected to have all members of the given interface. /// All objects may have different types. /// An array of object which implements the interface. public static I[] Implement(params T[] objects) where I : class { if (objects == null) throw new ArgumentNullException("objects"); I[] result = new I[objects.Length]; for (int i = 0; i < objects.Length; i++) result[i] = Implement(objects[i]); return result; } private static bool _allowStaticMembers; public static bool AllowStaticMembers { get { return _allowStaticMembers; } set { _allowStaticMembers = value; } } #endregion #region Multiple Duck /// /// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. /// /// An interface type to implement. /// Array of types which expected to have all members of the given interface. /// The duck object type. public static Type GetDuckType(Type interfaceType, Type[] objectTypes) { if (interfaceType == null) throw new ArgumentNullException("interfaceType"); if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); Hashtable types = (Hashtable)_duckTypes[interfaceType]; if (types == null) { lock (_duckTypes.SyncRoot) { types = (Hashtable)_duckTypes[interfaceType]; if (types == null) _duckTypes.Add(interfaceType, types = new Hashtable()); } } object key = new CompoundValue(objectTypes); Type type = (Type)types[key]; if (type == null) { lock (types.SyncRoot) { type = (Type)types[key]; if (type != null || types.ContainsKey(key)) return type; type = TypeFactory.GetType( new CompoundValue(interfaceType, key), interfaceType, new DuckTypeBuilder(MustImplementAttribute.Aggregate, interfaceType, objectTypes)); types.Add(key, type); } } return type; } /// /// Implements the requested interface from supplied set of objects. /// /// An interface type to implement. /// Array of types which have all members of the given interface. /// When this parameter is set to null, the object type will be used. /// Array of objects which types expected to have all members of the given interface. /// An object which implements the interface. public static object Aggregate(Type interfaceType, Type[] baseObjectTypes,params object[] objs) { if (objs == null) throw new ArgumentNullException("objs"); if (baseObjectTypes == null) { baseObjectTypes = new Type[objs.Length]; for (int i = 0; i < objs.Length; i++) if (objs[i] != null) baseObjectTypes[i] = objs[i].GetType(); } else { if (baseObjectTypes.Length != objs.Length) throw new ArgumentException(Resources.DuckTyping_InvalidNumberOfObjs, "baseObjectTypes"); for (int i = 0; i < objs.Length; i++) { Type objType = objs[i].GetType(); if (!TypeHelper.IsSameOrParent(baseObjectTypes[i], objType)) throw new ArgumentException( string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectTypes[i].FullName), "objs"); } } Type duckType = GetDuckType(interfaceType, baseObjectTypes); if (duckType == null) return null; object duck = TypeAccessor.CreateInstanceEx(duckType); ((DuckType)duck).SetObjects(objs); return duck; } /// /// Implements the requested interface from supplied set of objects. /// /// An interface type to implement. /// Array of object which types expected to have of the given interface. /// An object which implements the interface. public static object Aggregate(Type interfaceType,params object[] objs) { return Aggregate(interfaceType, (Type[])null, objs); } /// /// Implements the requested interface from supplied set of objects. /// /// An interface type to implement. /// Array of object which type expected to have all members of the given interface. /// An object which implements the interface. public static I Aggregate(params object[] objs) where I : class { return (I)Aggregate(typeof(I), null, objs); } #endregion } }