diff Source/Patterns/DuckTyping.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Patterns/DuckTyping.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,357 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+using BLToolkit.Common;
+using BLToolkit.Properties;
+using BLToolkit.Reflection;
+using BLToolkit.TypeBuilder;
+using BLToolkit.TypeBuilder.Builders;
+
+namespace BLToolkit.Patterns
+{
+	/// <summary>
+	/// 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 objects class does not include the interface
+	/// in its implements clause.
+	/// </summary>
+	public static class DuckTyping
+	{
+		#region Single Duck
+
+		static readonly Dictionary<Type,Dictionary<object,Type>> _duckTypes = new Dictionary<Type,Dictionary<object,Type>>();
+
+		/// <summary>
+		/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="objectType">Any type which expected to have all members of the given interface.</param>
+		/// <returns>The duck object type.</returns>
+		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");
+
+			Dictionary<object,Type> types;
+
+			lock(_duckTypes)
+				if (!_duckTypes.TryGetValue(interfaceType, out types))
+					_duckTypes.Add(interfaceType, types = new Dictionary<object,Type>());
+
+			Type type;
+
+			lock (types) if (!types.TryGetValue(objectType, out type))
+			{
+				type = TypeFactory.GetType(
+					new CompoundValue(interfaceType, objectType),
+					interfaceType, //objectType,
+					new DuckTypeBuilder(MustImplementAttribute.Default, interfaceType, new[] { objectType }));
+
+				types.Add(objectType, type);
+			}
+
+			return type;
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="baseObjectType">Any type which has all members of the given interface.
+		/// When this parameter is set to null, the object type will be used.</param>
+		/// <param name="obj">An object which type expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static object Implement(Type interfaceType, Type baseObjectType, object obj)
+		{
+			if (obj == null) throw new ArgumentNullException("obj");
+
+			var objType = obj.GetType();
+
+			if (TypeHelper.IsSameOrParent(interfaceType, objType))
+				return obj;
+
+			if (obj is DuckType)
+			{
+				var 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");
+
+			var duckType = GetDuckType(interfaceType, baseObjectType);
+
+			if (duckType == null)
+				return null;
+
+			var duck = TypeAccessor.CreateInstanceEx(duckType);
+
+			((DuckType)duck).SetObjects(obj);
+
+			return duck;
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="obj">An object which type expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static object Implement(Type interfaceType, object obj)
+		{
+			return Implement(interfaceType, null, obj);
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="baseObjectType">Any type which has all members of the given interface.
+		/// When this parameter is set to null, the object type will be used.</param>
+		/// <param name="objects">An object array which types expected to have all members of the given interface.
+		/// All objects may have different types.</param>
+		/// <returns>An array of object which implements the interface.</returns>
+		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;
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="objects">An object array which types expected to have all members of the given interface.
+		/// All objects may have different types.</param>
+		/// <returns>An array of object which implements the interface.</returns>
+		public static object[] Implement(Type interfaceType, params object[] objects)
+		{
+			return Implement(interfaceType, (Type)null, objects);
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <typeparam name="I">An interface type to implement.</typeparam>
+		/// <param name="obj">An object which type expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static I Implement<I>(object obj)
+			where I : class
+		{
+			return (I)Implement(typeof(I), null, obj);
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <typeparam name="I">An interface type to implement.</typeparam>
+		/// <typeparam name="T">Any type which has all members of the given interface.</typeparam>
+		/// <param name="obj">An object which type expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static I Implement<I,T>(T obj)
+			where I : class
+		{
+			return (I)Implement(typeof(I), typeof(T), obj);
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <typeparam name="I">An interface type to implement.</typeparam>
+		/// <param name="objects">An object array which types expected to have all members of the given interface.
+		/// All objects may have different types.</param>
+		/// <returns>An array of object which implements the interface.</returns>
+		public static I[] Implement<I>(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<I>(objects[i]);
+
+			return result;
+		}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		/// <typeparam name="I">An interface type to implement.</typeparam>
+		/// <typeparam name="T">Any type which has all members of the given interface.</typeparam>
+		/// <param name="objects">An object array which types expected to have all members of the given interface.
+		/// All objects may have different types.</param>
+		/// <returns>An array of object which implements the interface.</returns>
+		public static I[] Implement<I,T>(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<I,T>(objects[i]);
+
+			return result;
+		}
+
+		private static bool _allowStaticMembers;
+		public  static bool  AllowStaticMembers
+		{
+			get { return _allowStaticMembers;  }
+			set { _allowStaticMembers = value; }
+		}
+
+		#endregion
+
+		#region Multiple Duck
+
+		/// <summary>
+		/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="objectTypes">Array of types which expected to have all members of the given interface.</param>
+		/// <returns>The duck object type.</returns>
+		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");
+
+			Dictionary<object,Type> types;
+
+			lock (_duckTypes)
+				if (!_duckTypes.TryGetValue(interfaceType, out types))
+					_duckTypes.Add(interfaceType, types = new Dictionary<object,Type>());
+
+			object key = new CompoundValue(objectTypes);
+			Type   type;
+
+			lock (types) if (!types.TryGetValue(key, out type))
+			{
+				type = TypeFactory.GetType(
+					new CompoundValue(interfaceType, key),
+					interfaceType,
+					new DuckTypeBuilder(MustImplementAttribute.Aggregate, interfaceType, objectTypes));
+
+				types.Add(key, type);
+			}
+
+			return type;
+		}
+
+		/// <summary>
+		/// Implements the requested interface from supplied set of objects.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="baseObjectTypes">Array of types which have all members of the given interface.
+		/// When this parameter is set to null, the object type will be used.</param>
+		/// <param name="objs">Array of objects which types expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		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;
+		}
+
+		/// <summary>
+		/// Implements the requested interface from supplied set of objects.
+		/// </summary>
+		/// <param name="interfaceType">An interface type to implement.</param>
+		/// <param name="objs">Array of object which types expected to have of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static object Aggregate(Type interfaceType,params object[] objs)
+		{
+			return Aggregate(interfaceType, (Type[])null, objs);
+		}
+
+		/// <summary>
+		/// Implements the requested interface from supplied set of objects.
+		/// </summary>
+		/// <typeparam name="I">An interface type to implement.</typeparam>
+		/// <param name="objs">Array of object which type expected to have all members of the given interface.</param>
+		/// <returns>An object which implements the interface.</returns>
+		public static I Aggregate<I>(params object[] objs)
+			where I : class
+		{
+			return (I)Aggregate(typeof(I), null, objs);
+		}
+
+		#endregion
+	}
+}