Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Some Useful Concurrency Classes and A Small Testbench

Rate me:
Please Sign up or sign in to vote.
4.92/5 (37 votes)
15 Jan 2007CPOL70 min read 102.3K   1K   140  
Useful concurrency classes and small test bench in C#

#region Using Directives

using System;
using System.Reflection;

#endregion

#region MPFramework.AppCore.PlatformUtilities
///////////////////////////////////////////////////////////////////////////
//// Need Platform defs and the Attribute from PlatformUtilities.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.PlatformUtilities
{
	#region Enums
	/// <summary>
	/// This enumeration describes the platforms supported by a given method
	/// or assembly, etc.. It is a flags enum since multiple platforms can be
	/// supported by a given item.
	/// </summary>
	/// <remarks>
	/// Note we don't use anything from PlatformID, since it is not platform-independent.
	/// Silly, Huh? Also, we never want to support Win32s or anything lower
	/// than NT.
	/// </remarks>
	// New suggestions have been Portable .Net and Intel. Well, OK, somebody has
	// to sign up to do it!!! Don't everybody talk at once.... (KRM)
	[System.Flags]
	public enum CLRPlatformsSupported
	{
		/// <summary>
		/// Good old Microsoft Windows NT. Server and XP are same for now.
		/// Leave room for divergence later.....
		/// </summary>
		// (KRM) Prob'ly want to do Server-specific stuff the usual way in config.
		// files.
		MSWNT = 1,
		/// <summary>
		/// Microsoft CE.
		/// </summary>
		// We might do something with this if MS ever gets decent RT support
		// of more than managed c++. Look at Java RTS, Microsoft!!
		MSWCE = 4,
		/// <summary>
		/// For our courses.
		/// </summary>
		Rotor = 64,
		/// <summary>
		/// Microsoft running Mono.
		/// </summary>
		// We don't have any apps running the Mono CLI on Windows (we are not crazy),
		// but it sure is nice to have it there for preliminary testing of Mono stuff!!
		// We have finally settled the deep philosophical question: Is Mono's
		// port to MSWindows a "Windows" category? We decided no....
		MSMono = 128,
		/// <summary>
		/// Microsoft versions - 8 bits. Anything running on a MS OS.
		/// </summary>
		MicrosoftMask = 255,
		/// <summary>
		/// Microsoft Windows versions - 5 bits.
		/// </summary>
		MicrosoftWindowsMask = 31,
		/// <summary>
		/// Linux running Mono. Just dealing with WS for now.
		/// </summary>
		LinuxMono = 256,
		/// <summary>
		/// BSD's compilation/augmentation of Rotor.
		/// </summary>
		// (For Clem).
		BSDRotor = 512,
		/// <summary>
		/// Unix versions (including Linux, et. al.) - 10 bits.
		/// </summary>
		UnixMask = 1023 << 8
	}
	#endregion
	#region Attributes
	/// <summary>
	/// <para>
	/// This attribute is supplied to indicate any assemblies, classes methods, etc.
	/// that are platform-specific. It is useful in situations where it is difficult
	/// to move platform-specific code into separate assemblies and provides a useful
	/// marker for these items. The MPFramework can scan all assemblies in an AppDomain
	/// when the AppDomain is loaded to check whether an item (class, method, etc.) is
	/// not compatible with the current platform.
	/// </para>
	/// <para>
	/// Note that many methods adorned with this attribute have the "__currentPlatform"
	/// set as the property. Generally this means that the logic of the method is
	/// platform-independent, but data referenced may be platform-dependent.
	/// </para>
	/// </summary>
	[Serializable]
	class PlatformSpecificAttribute : Attribute
	{
		#region Class Fields
		static CLRPlatformsSupported _platformsSupported = 0;
		#endregion // Class Fields
		#region Constructors
		/// <summary>
		/// Default constructor just constructs it with a default value of 0.
		/// Just use <c>[PlatformSpecific]</c> if the item is to simply be flagged
		/// as being platform-dependent in some way.
		/// </summary>
		public PlatformSpecificAttribute() { }
		#endregion // Constructors
		#region Properties
		/// <summary>
		/// Sets/gets the enum. Examples:
		/// <list type="bullet">
		/// <item>
		/// <description>
		/// Use <c>[PlatformSpecific(PlatformsSupported = CLRPlatformsSupported.MSWNT)]</c>
		/// to decorate an item that is supported under WindowsNT.
		/// </description>
		/// </item>
		/// <item>
		/// <description>
		/// Use <c>[PlatformSpecific(PlatformsSupported
		///		= CLRPlatformsSupported.MicrosoftWindowsMask &amp; CLRPlatformsSupported.LinuxMono)]</c>
		/// to indicate support for all Windows platforms and Mono on Linux.
		/// </description>
		/// </item>
		/// </list>
		/// </summary>
		public CLRPlatformsSupported PlatformsSupported
		{
			get { return _platformsSupported; }
			set { _platformsSupported = value; }
		}
		#endregion // Properties
	}
	#endregion // Attributes
}
#endregion // MPFramework.AppCore.PlatformUtilities

#region MPFramework.AppCore.Manufacturing
///////////////////////////////////////////////////////////////////////////
//// Few things from Manufacturing that Article needs.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.Manufacturing
{
	#region Class ManufacturingUtils
	/// <summary>
	/// Helper classes/methods for manufacturing Types in AppDomains.
	/// </summary>
	public class ManufacturingUtils
	{
		#region Class Fields
		//		ManufacturingErrorHandler m_manufacturintErrorHandler
		#region Errors
		/// <summary>
		/// A constructor had bad construction parameters.
		/// </summary>
		// For copy/stub - need just a string
		// static public readonly MPError BadConstructionParamsError;
		static public readonly string BadConstructionParamsError
			= "Bad Construction Parameters";
		#endregion // Errors
		#region Binding Flags
		/// <summary>
		/// This set of binding flags is used is for looking up items on a
		/// type when we want to get all non-static public items.
		/// </summary>		
		static public readonly BindingFlags PublicInstanceBindingFlags
			= BindingFlags.Public | BindingFlags.Instance;
		/// <summary>
		/// This set of binding flags is used is for looking up items on a
		/// type when we want to get all non-static items.
		/// </summary>
		static public readonly BindingFlags PublicOrPrivateInstanceBindingFlags
			= BindingFlags.Instance | BindingFlags.Public |
				BindingFlags.NonPublic;
		/// <summary>
		/// This set of binding flags is used is for looking up constituents on a
		/// type when we want to get 'em all.
		/// </summary>
		static public readonly BindingFlags PublicPrivateStaticInstanceBindingFlags
			= BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
				BindingFlags.NonPublic;
		/// <summary>
		/// This set of binding flags is used for grabbing all fields, etc. in
		/// an inheritance hierarchy.
		/// </summary>
		static public BindingFlags FlattenAndDisplayAllInstancesBindingFlags
			= BindingFlags.Instance | BindingFlags.Public |
				BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
		#endregion // Binding Flags
		#endregion // Class Fields
		/// <summary>
		/// Easiest is just to make this a nested class for the stub.
		/// </summary>
		public static class ManufacturingErrorHandler
		{
			/// <summary>
			/// Stub for tests.
			/// </summary>
			public static void ProcessError(/* MPError */string instanceOfMPError, Exception thrownException,
				Exception exceptionToThrow, bool showDetail, string userPrependString,
					string userAppendString)
			{
				string printString;

				if(userPrependString == null)
					// No user string, so create it here.
					userPrependString = String.Empty;
				if(userAppendString == null)
					// No user string, so create it here.
					userAppendString = String.Empty;

				printString = instanceOfMPError + userPrependString + userPrependString;
				Console.WriteLine(printString);
				throw new ApplicationException();
			}
		}
	#endregion //  Class ManufacturingUtils
	}
}
#endregion //  MPFramework.AppCore.Manufacturing

#region MPFramework.AppCore.Manufacturing.TypeHandling
///////////////////////////////////////////////////////////////////////////
//// Need DoesSecondTypeInheritFrom FirstType() from TypeHandling.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.Manufacturing.TypeHandling
{
	#region Delegates
	/// <summary>
	/// This is a delegate for filtering accessor objects (PropertyDescriptors, FieldInfos,
	/// etc.) according to specific conditions on their <see cref="System.Attributes"/>.
	/// The delegate is obviously very general, but is named according to it's original
	/// purpose. Methods like <see cref="ICustomTypeDescriptor.GetProperties(Attributes[])"/>
	/// offer filtering only based on the presence of certain attribute Types. This delegate
	/// is designed to allow clients to perform more general filtering based on attributes.
	/// </summary>
	/// <param name="accessor">
	/// This is a constituent accessor that may have attributes attached to it. It may be
	/// <c>null</c>, in which case the method returns <c>true</c>.
	/// </param>
	/// <param name="attributes">
	/// This is an array of <see cref="System.Attribute"/>s that are to be evaluated
	/// as to whether a given accessor "passes" the criteria for inclusion. It may
	/// be <c>null</c>, in which case the method returns <c>true</c>.
	/// </param>
	/// <returns>
	/// <c>true</c> if the accessor passes the criteria.
	/// </returns>
	/// <remarks>
	/// A simple example of a useful criterion that is not available in the
	/// CLR is to check to see whether a given accessor does NOT have a certain
	/// attribute. Much more complex scenarios are possible as in the
	/// <see cref="LayeredSerializer"/>.
	/// </remarks>
	public delegate bool FilterAccessorByAttributes(object accessor, Attribute[] attributes);
	#endregion // Delegates
	#region Class TypeHandlingUtils
	/// <summary>
	/// Helper classes/methods for handling Types in AppDomains.
	/// </summary>
	public class TypeHandlingUtils
	{
		// Need a couple errors for tests.
		#region Class Fields
		/// <summary>
		/// A client attempted to add a Type-specific Type handler factory to
		/// a provider and neither the provider or the factory could produce
		/// the factory's Type.
		/// </summary>
		// static public readonly MPError ProviderOrFactoryCantCreateTypeError;
		static public readonly string ProviderOrFactoryCantCreateTypeError
			= "ProviderOrFactoryCantCreateTypeError";
		/// <summary>
		/// A search for a named FieldInfo did not return a FieldInfo.
		/// </summary>
		//static public readonly MPError FailureToLocateFieldInfoError;
		static public readonly string FailureToLocateFieldInfoError
			= "FailureToLocateFieldInfoError";
		/// <summary>
		/// Failure casting one Type to another.
		/// </summary>
		//static public readonly MPError CastFailureError;
		static public readonly string CastFailureError
			= "CastFailureError";
		// Couple copies for static constants.
		static Type TypeOfMemberInfo = typeof(MemberInfo);
		static Type TypeOfFieldInfo = typeof(FieldInfo);
		static Type TypeOfAttribute = typeof(Attribute);
		#endregion // Class Fields

		/// <summary>
		/// This method checks a given Type to see if it inherits from another
		/// given Type.
		/// </summary>
		/// <param name="inheritedType">
		/// This Type may be either an interface or a struct or class. If it is a
		/// struct, the method returns <c>false</c>. If it is class, and the
		/// <see paramref="typeToCheck"/> is an interface or struct, the method returns
		/// <c>false</c>.
		/// </param>
		/// <param name="typeToCheck">
		/// If this Type is a struct, the method returns <c>false</c> if the
		/// <see paramref="inheritedType"/> is not an interface.
		/// </param>
		/// <returns>
		/// Indicates whether <see paramref name="typeToCheck"> inherits from
		/// <see paramref name="inheritedType">.
		/// </returns>
		/// <remarks>
		/// Note, unfortunately, that this method is NOT lightweight. MS's GetInterfaces
		/// method generates a new Type[] array every time.
		/// </remarks>
		public static bool DoesSecondTypeInheritFromFirstType(Type inheritedType, Type typeToCheck)
		{
			// Interface collection is always flat. Just loop over them.
			foreach(Type iface in typeToCheck.GetInterfaces()) {
				if(iface == inheritedType) {
					return true;
				}
			}
			// Gotta' climb the inheritance tree.
			while(typeToCheck != typeof(System.Object)) {
				if(inheritedType == typeToCheck) {
					return true;
				}
				typeToCheck = typeToCheck.BaseType;
			}
			return false;
		}
		/// <summary>
		/// Uses reflection to retrieve a specific <see cref="FieldInfo"/> for a field member
		/// on a Type. This method has the ability to filter <see cref="FieldInfo"/> elements
		/// according to general attribute settings.
		/// </summary>
		/// <param name="hostObjectType">
		/// This is the Type of the object hosting the field. This cannot be <c>null</c>.
		/// </param>
		/// <param name="fieldName">
		/// This name is the textual name of the field as known by the host,
		/// not the fully-qualified name of the Type, such as <see cref="System.Drawing.Size"/>.
		/// An example would be "autoScaleBaseSize" on a <see cref="System.Windows.Forms.Form"/>
		/// host object. This cannot be <c>null</c>.
		/// </param>
		/// <param name="attributes">
		/// This is an array of <see cref="System.Attribute"/>s that are returned
		/// field is to be evaluated against within the <see cref="attributeFilter"/>.
		/// If the field fails the evaluation, it is not returned.
		/// </param>
		/// <param name="attributeFilter">
		/// This is an <see cref="FilterAccessorByAttributes"/> delegate that will be
		/// used to evaluate the descriptor against the supplied set of attributes.
		/// This filter must understand how to pull attributes from properties and
		/// examine them. If this filter is <c>null</c> and attributes is
		/// non-<c>null</c>, the standard CLR style attribute filtering is
		/// applied. With this standard filtering, <see cref="FieldInfo"/>s are returned
		/// only if they have attributes on the list of <see cref="attributes"/> supplied.
		/// With this standard filtering, a single attribute match will qualify the field
		/// for return.
		/// </param>
		/// <param name="bindingFlags">
		/// These flags tell the CLR how to access fields on the Type. User can specify
		/// public/private, etc.. See <see cref="System.Reflection.BindingFlags"/>.
		/// </param>
		/// <param name="throwException">
		/// True to throw an exception when the named field cannot be located. This check
		/// is made before filtering. Filtering may also cause the method to return
		/// <c>null</c> if a located field does not meet the filtering criteria.
		/// </param>
		/// <returns>
		/// A FieldInfo object, if one is found that passes the filtering criteria
		/// or <c>null</c> if one is not found.
		/// </returns>
		/// <exception cref="ApplicationException">
		/// When a <see cref="FieldInfo"/> with <see cref="fieldName"/> cannot be
		/// found on the <see cref="hostType"/>, an exception: FailureToLocateFieldInfoError
		/// is thrown if <see cref="throwException"/> is <c>true</c>.
		/// </exception>
		public static FieldInfo GetFieldInfo(Type hostObjectType, string fieldName,
			Attribute[] attributes, FilterAccessorByAttributes attributeFilter,
				BindingFlags bindingFlags, bool throwException)
		{
			// Grab it if it's here.
			FieldInfo fieldInfo = hostObjectType.GetField(fieldName, bindingFlags);
			// Got nothing is a problem if throwing exceptions.
			if((fieldInfo == null) && (throwException)) {
				TypeHandlingUtils.TypeHandlingErrorHandler.ProcessError(
					TypeHandlingUtils.FailureToLocateFieldInfoError, null,
						null, true, null,
							"\nTYPE: " + hostObjectType.ToString() +
								"\nFIELDNAME: " + fieldName + "\n");
			}
			// If we have no custom filter, just do the standard CLR attribute filter if we
			// have attributes - otherwise use our custom filter.
			if(attributes != null) {
				// Plug in our standard filter if we need it.
				if(attributeFilter == null)
					attributeFilter = TypeHandlingUtils.FilterMembersByAnyAttributePresence;
				// Apply the filter.
				if(attributeFilter(fieldInfo, attributes)) return fieldInfo;
				// Nope, didn't pass.
				return null;
			}
			// No filtering at all - just return unconditionally.
			return fieldInfo;
		}

		/// <summary>
		/// This is a method for filtering <see cref="MemberInfo"/> objects according to
		/// the presence or non presence of certain <see cref="System.Attribute"/>s. The
		/// whole reason that we built this is that the CLR does not have a built-in
		/// filtering option such as GetFields(BindingFlags, Attribute[]). This method
		/// tests whether attributes match based on a<c>typeof(Attribute)</c> check.
		/// </summary>
		/// <param name="memberObject">
		/// This is a <see cref="MemberInfo"/> that may have attributes attached to it.
		/// It may be <c>null</c>, in which case the method returns <c>true</c>.
		/// </param>
		/// <param name="attributes">
		/// This is an array of <see cref="System.Attribute"/>s that must be present on
		/// the incoming field in order for it to "pass" the test criteria. If this argument
		/// is <c>null</c>, the method always returns <c>true</c>. If
		/// any of the attributes in this array is present on the field, it passes the test
		/// and <c>true</c> is returned.
		/// </param>
		/// <returns>
		/// <c>true</c> if the field passes the criteria.
		/// </returns>
		/// <exception> <see cref="ApplicationException"/> is thrown:
		/// <c>"Problem casting between Types."</c> if the
		/// <see paramref="memberObject"/> is not a <see cref="MemberInfo"/> Type.
		/// </exception>
		public static bool FilterMembersByAnyAttributePresence(System.Object memberObject,
			Attribute[] attributes)
		{
			MemberInfo memberInfo;
			// Get out if nothing to do.
			if((attributes == null) || (memberObject == null)) return true;
			// Can't survive a problem with the wrong Type.
			if((memberInfo = (memberObject as MemberInfo)) == null) {
				TypeHandlingUtils.TypeHandlingErrorHandler.ProcessError(
					TypeHandlingUtils.CastFailureError, null,
						null, true, null,
							"\nFROMTYPE: " + memberObject.GetType().ToString() +
								"\nTOTYPE: " + TypeOfMemberInfo.ToString() + "\n");
			}
			// Double loop - if we collide with anything, we pass.
			foreach(Attribute memberAttribute in memberInfo.GetCustomAttributes(TypeOfAttribute, true))
				foreach(Attribute incomingAttribute in attributes)
					if(incomingAttribute.GetType() == memberAttribute.GetType()) return true;
			// Doesn't have any of the attributes we require, so forget it.
			return false;
		}
		/// <summary>
		/// Easiest is just to make this a nested class for the stub. Same
		/// deal as ManufacturingErrorHandler - but just call
		/// ManufacturingErrorHandler.
		/// </summary>
		public static class TypeHandlingErrorHandler
		{
			/// <summary>
			/// Stub for tests.
			/// </summary>
			public static void ProcessError(/* MPError */string instanceOfMPError, Exception thrownException,
				Exception exceptionToThrow, bool showDetail, string userPrependString,
					string userAppendString)
			{
				ManufacturingUtils.ManufacturingErrorHandler.ProcessError
					(instanceOfMPError, thrownException,
						exceptionToThrow, showDetail, userPrependString,
							userAppendString);
			}
		}
	}
	#endregion // Class TypeHandlingUtils
}
#endregion //  MPFramework.AppCore.Manufacturing.TypeHandling

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Kurt R. Matis received the B.S degree in Applied Mathemetics from Empire State College in 1981 and the PhD. degree in Electrical Engineering from Rensselaer Polytechnic Institute in 1984. He has been involved in several companies over the past 30 years, but has been most recently involved with the Macher-Plander Software Engineering Consortium, of which he is a co-founder. The Consortium is involved with education in .Net technologies and Software Quality Management topics.

Dr. Matis is a member of IEEE and the American Historical Truck Society. Kurt lives happily in Troy, NY with his beautiful wife, two beautiful daughters and his beautiful trucks.

Dr. Matis is interested in working with companies who wish assistance in porting legacy applications of all types to .Net. He can be reached at krogerma@aol.com.




Comments and Discussions