Click here to Skip to main content
15,891,607 members
Articles / Programming Languages / C# 4.0

ReflectionHelper

Rate me:
Please Sign up or sign in to vote.
4.93/5 (31 votes)
6 Aug 2012CPOL4 min read 72.6K   2.2K   79  
This class makes getting MemberInfos easy, without the use of magic strings (so it is refactoring friendly) and also allows you to create delegates to do fast accesses to those items, much faster than the normal Invoke, GetValue or SetValue methods.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;

namespace Pfz.Threading.Cooperative
{
	/// <summary>
	/// Class that simulates a stack-saving mechanism (which does not exist in .Net).
	/// If in the future such mechanism exists, this class may be killed or simple redirect to it.
	/// At this moment, the "saved stack" runs in another thread completely synchronized with
	/// the main one. But this can cause dead-locks if the caller holds a lock that the calle
	/// should also use.
	/// Aside from that, you can YieldReturn at any moment, even inside sub-methods that are
	/// not enumerable.
	/// </summary>
	public sealed class UnsafeStackSaver:
		IAdvancedDisposable,
		IEnumerable<bool>,
		IEnumerator<bool>
	{
		#if !SILVERLIGHT
		internal static readonly object _syncAbortToken = new object();
		#endif

		#if DEBUG
			private Thread _thisThread;
		#endif

		private readonly object _executePulser = new object();
		private Action _action;
		private bool _wasDisposed;
		private bool _executing;
		
		/// <summary>
		/// Creates a new StackSaver that will run the given action, allowing it to
		/// YieldReturn at any moment. It will not generate values, so it is used as
		/// a synchronization mechanism.
		/// </summary>
		public UnsafeStackSaver(Action action)
		{
			_action = action;

			UnlimitedThreadPool.Run(_Start);
		}
		
		/// <summary>
		/// Releases the stack-saver. It will kill the executing method the same way a thread is aborted
		/// (with a ThreadAbortException, calling only catches and finallys).
		/// </summary>
		public void Dispose()
		{
			lock(_executePulser)
			{
				_wasDisposed = true;
				Monitor.Pulse(_executePulser);
				
				while(_action != null)
					Monitor.Wait(_executePulser);
			}
		}
		
		/// <summary>
		/// Gets a value indicating if a Dispose call was already made on this object,
		/// even if it was not finalized yet.
		/// </summary>
		public bool WasDisposed
		{
			get
			{
				return _wasDisposed;
			}
		}
		
		/// <summary>
		/// Gets the exception that finished this stack-saver.
		/// </summary>
		public Exception Exception { get; private set; }

		[ThreadStatic]
		private static UnsafeStackSaver _current;
		/// <summary>
		/// When inside the action of a stack-saver, the stack-saver can be discovered by this.
		/// If the return is null, then this method was called directly or by a value-producing stack-saver.
		/// </summary>
		public static UnsafeStackSaver Current
		{
			get
			{
				return _current;
			}
		}
		
		[ThreadStatic]
		internal static IValueProducerStackSaver _currentValueProducer;
		/// <summary>
		/// When inside the action of a stack-saver that produces values, the stack-saver can 
		/// be discovered by this. It will use a base interface, so you don't need to know its exact type
		/// (it will work as long as you produce castable values).
		/// If the return is null, then this method was called directly or by a non-value producing stack-saver.
		/// </summary>
		public static IValueProducerStackSaver CurrentValueProducer
		{
			get
			{
				return _currentValueProducer;
			}
		}

		private void _Start()
		{
			try
			{
				var thisThread = Thread.CurrentThread;
				thisThread.IsBackground = true;

				#if DEBUG
					_thisThread = thisThread;
				#endif

				_current = this;
		
				lock(_executePulser)
					_Wait();
					
				_action();
			}
			#if !SILVERLIGHT
			catch(ThreadAbortException abortException)
			{
				// if it is not our abort, rethrow.
				if (abortException.ExceptionState != _syncAbortToken)
				{
					Exception = abortException;
					throw;
				}

				// if it is our abort, it was used only to arrive here, so
				// now we can resetabort and let the thread return to the pool.
				Thread.ResetAbort();
			}
			#endif
			catch(Exception exception)
			{
				Exception = exception;
			}
			finally
			{
				#if DEBUG
					_thisThread = null;
				#endif

				_current = null;
				_action = null;
				_executing = false;
				
				lock(_executePulser)
					Monitor.PulseAll(_executePulser);
			}
		}

		/// <summary>
		/// Yield returns to a stack-saver that does not produce values, considering there is one.
		/// If a stack-saver does not exist, this method throws an InvalidOperationException.
		/// </summary>
		public static void StaticYieldReturn()
		{
			var stackSaver = _current;
			if (stackSaver == null)
				throw new InvalidOperationException("This method is not managed by a non-returning stack-saver.");
				
			stackSaver.YieldReturn();
		}

		/// <summary>
		/// Yield returns to a typed stack-saver, considering there is one.
		/// If a stack-saver does not exist, an InvalidOperationException is thrown.
		/// Note that to return a string as an object, you should not allow
		/// the type-inference.
		/// </summary>
		public static void StaticYieldReturn<T>(T value)
		{
			var instance = UnsafeStackSaver<T>._current;
			if (instance != null)
			{
				instance.YieldReturn(value);
				return;
			}
			
			var interfaced = _currentValueProducer;
			if (interfaced == null)
				throw new InvalidOperationException("This method is not managed by any returning stack-saver.");
				
			interfaced.YieldReturn(value);
		}
		
		/// <summary>
		/// YieldReturns to the caller of this stack-saver.
		/// </summary>
		public void YieldReturn()
		{
			#if DEBUG
				if (Thread.CurrentThread != _thisThread)
					throw new InvalidOperationException("Yield return may only be called by the action thread when it executes.");
			#endif
				
			lock(_executePulser)
			{
				_executing = false;
				Monitor.Pulse(_executePulser);

				_Wait();
			}
		}

		private void _Wait()
		{
			while(true)
			{
				if (_wasDisposed)
				{
					#if SILVERLIGHT
						Thread.CurrentThread.Abort();
					#else
						Thread.CurrentThread.Abort(_syncAbortToken);
					#endif
					// if the stacksaver was disposed, we must kill
					// the actual thread. Calling abort on the actual thread
					// does not cause an asynchronous exception, so it is safe.
				}
				
				if (_executing)
					return;
					
				Monitor.Wait(_executePulser);
			}
		}

		/// <summary>
		/// Executes the method of this stack-saver until it ends or until it calls
		/// a YieldReturn.
		/// Returns true if it YieldReturned, false if the action ended.
		/// If an exception is thrown on the called method, it is rethrown as a 
		/// TargetInvocationException.
		/// </summary>
		public bool Execute()
		{
			BeginExecute();
			return EndExecute();
		}

		/// <summary>
		/// Executes the method of this stack-saver until it ends or until it calls
		/// a YieldReturn.
		/// Returns true if it YieldReturned, false if the action ended.
		/// If an exception is thrown, it is stored in the Exception property.
		/// </summary>
		public bool ExecuteNoException()
		{
			BeginExecute();
			return EndExecuteNoException();
		}

		/// <summary>
		/// Starts the execution of the method of this StackSaver in parallel.
		/// </summary>
		public void BeginExecute()
		{
			lock(_executePulser)
			{
				if (_executing)
					throw new InvalidOperationException("This object is already executing.");
					
				if (_action == null)
					throw new InvalidOperationException("The stacksaver already finished its execution.");
					
				if (_wasDisposed)
					throw new ObjectDisposedException(GetType().FullName);
					
				_executing = true;
				Monitor.Pulse(_executePulser);
			}
		}

		/// <summary>
		/// Waits until the action executed by the BeginExecute finishes.
		/// If the action throws an exception, it is rethrown in this thread as a TargetInvocationException.
		/// </summary>
		/// <returns>true if the action yield returned, false if it ended.</returns>
		public bool EndExecute()
		{
			bool result = EndExecuteNoException();

			var exception = Exception;
			if (exception != null)
				throw new TargetInvocationException(exception);

			return result;
		}

		/// <summary>
		/// Waits until the action executed by the BeginExecute finishes.
		/// If an exception is thrown on the other thread, it is rethrown here as a TargetInvocationException.
		/// </summary>
		/// <returns>true if the action yield returned, false if it ended.</returns>
		public bool EndExecuteNoException()
		{
			lock(_executePulser)
			{
				while(_executing)
					Monitor.Wait(_executePulser);
					
				return _action != null;
			}
		}

		#region IEnumerable<bool> Membres
			IEnumerator<bool> IEnumerable<bool>.GetEnumerator()
			{
				return this;
			}
		#endregion
		#region IEnumerable Membres
			IEnumerator IEnumerable.GetEnumerator()
			{
				return this;
			}
		#endregion

		#region IEnumerator<bool> Membres
			bool IEnumerator<bool>.Current
			{
				get
				{
					return true;
				}
			}
		#endregion
		#region IEnumerator Membres
			internal static readonly object BoxedTrue = true;
			object IEnumerator.Current
			{
				get
				{
					return BoxedTrue;
				}
			}
			bool IEnumerator.MoveNext()
			{
				return Execute();
			}
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		#endregion
	}

	/// <summary>
	/// Class that simulates a stack-saving mechanism (which does not exist in .Net).
	/// If in the future such mechanism exists, this class may be killed or simple redirect to it.
	/// At this moment, the "saved stack" runs in another thread completely synchronized with
	/// the main one. But this can cause dead-locks if the caller holds a lock that the calle
	/// should also use.
	/// Aside from that, you can YieldReturn at any moment, even inside sub-methods that are
	/// not enumerable.
	/// </summary>
	public sealed class UnsafeStackSaver<T>:
		IAdvancedDisposable,
		IEnumerable<T>,
		IEnumerator<T>,
		IValueProducerStackSaver
	{
		#if DEBUG
			private Thread _thisThread;
		#endif

		private readonly object _executePulser = new object();
		private Action _action;
		private bool _executing;
		private bool _wasDisposed;
		
		/// <summary>
		/// Creates a new StackSaver that will run the given action, allowing it to
		/// YieldReturn at any moment. It will not generate values, so it is used as
		/// a synchronization mechanism.
		/// </summary>
		public UnsafeStackSaver(Action action)
		{
			_action = action;

			UnlimitedThreadPool.Run(_Start);
		}
		
		/// <summary>
		/// Releases the stack-saver. It will kill the executing method the same way a thread is aborted
		/// (with a ThreadAbortException, calling only catches and finallys).
		/// </summary>
		public void Dispose()
		{
			lock(_executePulser)
			{
				_wasDisposed = true;
				Monitor.Pulse(_executePulser);
				
				while(_action != null)
					Monitor.Wait(_executePulser);
			}
		}
		
		/// <summary>
		/// Gets a value indicating if a Dispose call was already made on this object,
		/// even if it was not finalized yet.
		/// </summary>
		public bool WasDisposed
		{
			get
			{
				return _wasDisposed;
			}
		}
		
		/// <summary>
		/// Gets the value generated by the last call to Execute.
		/// </summary>
		public T CurrentValue { get; private set; }

		/// <summary>
		/// Gets the exception that finished this stack-saver.
		/// </summary>
		public Exception Exception { get; private set; }

		[ThreadStatic]
		internal static UnsafeStackSaver<T> _current;
		/// <summary>
		/// When inside the action of a stack-saver, the stack-saver can be discovered by this.
		/// If the return is null, then this method was called directly or by a value-producing stack-saver.
		/// </summary>
		public static UnsafeStackSaver<T> Current
		{
			get
			{
				return _current;
			}
		}
		
		private void _Start()
		{
			try
			{
				var thisThread = Thread.CurrentThread;
				thisThread.IsBackground = true;

				#if DEBUG
					_thisThread = thisThread;
				#endif

				_current = this;
				UnsafeStackSaver._currentValueProducer = this;
		
				lock(_executePulser)
					_Wait();
					
				_action();
			}
			#if !SILVERLIGHT
			catch(ThreadAbortException abortException)
			{
				// if it is not our abort, rethrow.
				if (abortException.ExceptionState != UnsafeStackSaver._syncAbortToken)
				{
					Exception = abortException;
					throw;
				}

				// if it is our abort, it was used only to arrive here, so
				// now we can resetabort and let the thread return to the pool.
				Thread.ResetAbort();
			}
			#endif
			catch(Exception exception)
			{
				Exception = exception;
			}
			finally
			{
				#if DEBUG
					_thisThread = null;
				#endif

				_current = null;
				UnsafeStackSaver._currentValueProducer = null;
				_action = null;
				_executing = false;
				
				lock(_executePulser)
					Monitor.PulseAll(_executePulser);
			}
		}

		/// <summary>
		/// YieldReturns to the caller of this stack-saver.
		/// </summary>
		public void YieldReturn(T value)
		{
			#if DEBUG
				if (Thread.CurrentThread != _thisThread)
					throw new InvalidOperationException("Yield return may only be called by the action thread when it executes.");
			#endif
				
			lock(_executePulser)
			{
				_executing = false;
				CurrentValue = value;
				Monitor.Pulse(_executePulser);

				_Wait();
			}
		}

		private void _Wait()
		{
			while(true)
			{
				if (_wasDisposed)
				{
					#if SILVERLIGHT
						Thread.CurrentThread.Abort();
					#else
						Thread.CurrentThread.Abort(UnsafeStackSaver._syncAbortToken);
					#endif
					// if the stacksaver was disposed, we must kill
					// the actual thread. Calling abort on the actual thread
					// does not cause an asynchronous exception, so it is safe.
				}
				
				if (_executing)
					return;
					
				Monitor.Wait(_executePulser);
			}
		}

		/// <summary>
		/// Executes the method of this stack-saver until it ends or until it calls
		/// a YieldReturn.
		/// Returns true if it YieldReturned, false if the action ended.
		/// If an exception is thrown on the called method, it is rethrown as a 
		/// TargetInvocationException.
		/// </summary>
		public bool Execute()
		{
			BeginExecute();
			return EndExecute();
		}

		/// <summary>
		/// Executes the method of this stack-saver until it ends or until it calls
		/// a YieldReturn.
		/// Returns true if it YieldReturned, false if the action ended.
		/// If an exception is thrown, it is stored in the Exception property.
		/// </summary>
		public bool ExecuteNoException()
		{
			BeginExecute();
			return EndExecuteNoException();
		}

		/// <summary>
		/// Starts the execution of the method of this StackSaver in parallel.
		/// </summary>
		public void BeginExecute()
		{
			lock(_executePulser)
			{
				if (_executing)
					throw new InvalidOperationException("This object is already executing.");
					
				if (_action == null)
					throw new InvalidOperationException("The stacksaver already finished its execution.");
					
				if (_wasDisposed)
					throw new ObjectDisposedException(GetType().FullName);
					
				_executing = true;
				Monitor.Pulse(_executePulser);
			}
		}

		/// <summary>
		/// Waits until the action executed by the BeginExecute finishes.
		/// If the action throws an exception, it is rethrown in this thread as a TargetInvocationException.
		/// </summary>
		/// <returns>true if the action yield returned, false if it ended.</returns>
		public bool EndExecute()
		{
			bool result = EndExecuteNoException();

			var exception = Exception;
			if (exception != null)
				throw new TargetInvocationException(exception);

			return result;
		}

		/// <summary>
		/// Waits until the action executed by the BeginExecute finishes.
		/// If an exception is thrown on the other thread, it is rethrown here as a TargetInvocationException.
		/// </summary>
		/// <returns>true if the action yield returned, false if it ended.</returns>
		public bool EndExecuteNoException()
		{
			lock(_executePulser)
			{
				while(_executing)
					Monitor.Wait(_executePulser);
					
				return _action != null;
			}
		}

		#region IEnumerable<T> Membres
			IEnumerator<T> IEnumerable<T>.GetEnumerator()
			{
				return this;
			}
		#endregion
		#region IEnumerable Membres
			IEnumerator IEnumerable.GetEnumerator()
			{
				return this;
			}
		#endregion

		#region IEnumerator<T> Membres
			T IEnumerator<T>.Current
			{
				get
				{
					return CurrentValue;
				}
			}
		#endregion
		#region IEnumerator Membres
			internal static readonly object BoxedTrue = true;
			object IEnumerator.Current
			{
				get
				{
					return BoxedTrue;
				}
			}
			bool IEnumerator.MoveNext()
			{
				return Execute();
			}
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		#endregion
		#region IValueProducerStackSaver Membres
			void IValueProducerStackSaver.YieldReturn(object value)
			{
				YieldReturn((T)value);
			}
		#endregion
	}

}

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
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions