Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / WPF

INotifyPropertyChanged - Automagically Implemented (Reloaded)

Rate me:
Please Sign up or sign in to vote.
4.83/5 (17 votes)
25 Nov 2011CPOL4 min read 53K   655   46  
Implement INotifyPropertyChanged and change verifying in model using a proxy generator
using System;
using System.Configuration.Assemblies;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace Weaver.Emit
{
	/// <summary>
	/// A wrapper around the <see cref="AssemblyBuilder"/> and <see cref="ModuleBuilder"/> classes.
	/// </summary>
	/// <include file="Examples.CS.xml" path='examples/emit[@name="Emit"]/*' />
	/// <include file="Examples.VB.xml" path='examples/emit[@name="Emit"]/*' />
	/// <seealso cref="System.Reflection.Emit.AssemblyBuilder">AssemblyBuilder Class</seealso>
	/// <seealso cref="System.Reflection.Emit.ModuleBuilder">ModuleBuilder Class</seealso>
	public class AssemblyBuilderHelper
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="AssemblyBuilderHelper"/> class
		/// with the specified parameters.
		/// </summary>
		/// <param name="path">The path where the assembly will be saved.</param>
		public AssemblyBuilderHelper(string path) : this(path, null, null)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="AssemblyBuilderHelper"/> class
		/// with the specified parameters.
		/// </summary>
		/// <param name="path">The path where the assembly will be saved.</param>
		/// <param name="version">The assembly version.</param>
		/// <param name="keyFile">The key pair file to sign the assembly.</param>
		public AssemblyBuilderHelper(string path, Version version, string keyFile)
		{
			if (path == null) throw new ArgumentNullException("path");

			int idx = path.IndexOf(',');

			if (idx > 0)
			{
				path = path.Substring(0, idx);

				if (path.Length >= 200)
				{
					idx = path.IndexOf('`');

					if (idx > 0)
					{
						int idx2 = path.LastIndexOf('.');

						if (idx2 > 0 && idx2 > idx)
							path = path.Substring(0, idx + 1) + path.Substring(idx2 + 1);
					}
				}
			}

			path = path.Replace("+", ".");

			if (path.Length >= 260)
			{
				path = path.Substring(0, 248);

				for (int i = 0; i < int.MaxValue; i++)
				{
					string newPath = string.Format("{0}_{1:0000}.dll", path, i);

					if (!System.IO.File.Exists(newPath))
					{
						path = newPath;
						break;
					}
				}
			}

			string assemblyName = System.IO.Path.GetFileNameWithoutExtension(path);
			string assemblyDir  = System.IO.Path.GetDirectoryName(path);

			_path              = path;
			_assemblyName.Name = assemblyName;

			if (version != null)
				_assemblyName.Version = version;

			if (!string.IsNullOrEmpty(keyFile))
			{
				_assemblyName.Flags        |= AssemblyNameFlags.PublicKey;
				_assemblyName.KeyPair       = new StrongNameKeyPair(System.IO.File.OpenRead(keyFile));
				_assemblyName.HashAlgorithm = AssemblyHashAlgorithm.SHA1;
			}
#if DEBUG
			_assemblyName.Flags |= AssemblyNameFlags.EnableJITcompileTracking;
#else
			_assemblyName.Flags |= AssemblyNameFlags.EnableJITcompileOptimizer;
#endif

			_assemblyBuilder =
				string.IsNullOrEmpty(assemblyDir)?
				Thread.GetDomain().DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.RunAndSave):
				Thread.GetDomain().DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.RunAndSave, assemblyDir);

			_assemblyBuilder.SetCustomAttribute(BLToolkitAttribute);
		}

		private readonly string _path;
		/// <summary>
		/// Gets the path where the assembly will be saved.
		/// </summary>
		public  string  Path
		{
			get { return _path; }
		}

		private readonly AssemblyName _assemblyName = new AssemblyName();
		/// <summary>
		/// Gets AssemblyName.
		/// </summary>
		public           AssemblyName  AssemblyName
		{
			get { return _assemblyName; }
		}

		private readonly AssemblyBuilder _assemblyBuilder;
		/// <summary>
		/// Gets AssemblyBuilder.
		/// </summary>
		public           AssemblyBuilder  AssemblyBuilder
		{
			get { return _assemblyBuilder; }
		}

		/// <summary>
		/// Gets the path where the assembly will be saved.
		/// </summary>
		public  string  ModulePath
		{
			get { return System.IO.Path.GetFileName(Path); }
		}

		private ModuleBuilder _moduleBuilder;
		/// <summary>
		/// Gets ModuleBuilder.
		/// </summary>
		public  ModuleBuilder  ModuleBuilder
		{
			get 
			{
				if (_moduleBuilder == null)
				{
					_moduleBuilder = _assemblyBuilder.DefineDynamicModule(ModulePath);
					_moduleBuilder.SetCustomAttribute(BLToolkitAttribute);

				}

				return _moduleBuilder;
			}
		}

		private CustomAttributeBuilder _blToolkitAttribute;
		/// <summary>
		/// Retrieves a cached instance of <see cref="BLToolkit.TypeBuilder.BLToolkitGeneratedAttribute"/> builder.
		/// </summary>
		public  CustomAttributeBuilder  BLToolkitAttribute
		{
			get 
			{
				if (_blToolkitAttribute == null)
				{
					Type            at = typeof(TypeBuilder.BLToolkitGeneratedAttribute);
					ConstructorInfo ci = at.GetConstructor(Type.EmptyTypes);

					_blToolkitAttribute = new CustomAttributeBuilder(ci, new object[0]);
				}

				return _blToolkitAttribute;
			}
		}

		/// <summary>
		/// Converts the supplied <see cref="AssemblyBuilderHelper"/> to a <see cref="AssemblyBuilder"/>.
		/// </summary>
		/// <param name="assemblyBuilder">The <see cref="AssemblyBuilderHelper"/>.</param>
		/// <returns>An <see cref="AssemblyBuilder"/>.</returns>
		public static implicit operator AssemblyBuilder(AssemblyBuilderHelper assemblyBuilder)
		{
			if (assemblyBuilder == null) throw new ArgumentNullException("assemblyBuilder");

			return assemblyBuilder.AssemblyBuilder;
		}

		/// <summary>
		/// Converts the supplied <see cref="AssemblyBuilderHelper"/> to a <see cref="ModuleBuilder"/>.
		/// </summary>
		/// <param name="assemblyBuilder">The <see cref="AssemblyBuilderHelper"/>.</param>
		/// <returns>A <see cref="ModuleBuilder"/>.</returns>
		public static implicit operator ModuleBuilder(AssemblyBuilderHelper assemblyBuilder)
		{
			if (assemblyBuilder == null) throw new ArgumentNullException("assemblyBuilder");

			return assemblyBuilder.ModuleBuilder;
		}

		/// <summary>
		/// Saves this dynamic assembly to disk.
		/// </summary>
		public void Save()
		{
			_assemblyBuilder.Save(ModulePath);
		}

		#region DefineType Overrides

		/// <summary>
		/// Constructs a <see cref="TypeBuilderHelper"/> for a type with the specified name.
		/// </summary>
		/// <param name="name">The full path of the type.</param>
		/// <returns>Returns the created <see cref="TypeBuilderHelper"/>.</returns>
		/// <seealso cref="System.Reflection.Emit.ModuleBuilder.DefineType(string)">ModuleBuilder.DefineType Method</seealso>
		public TypeBuilderHelper DefineType(string name)
		{
			return new TypeBuilderHelper(this, ModuleBuilder.DefineType(name));
		}

		/// <summary>
		/// Constructs a <see cref="TypeBuilderHelper"/> for a type with the specified name and base type.
		/// </summary>
		/// <param name="name">The full path of the type.</param>
		/// <param name="parent">The Type that the defined type extends.</param>
		/// <returns>Returns the created <see cref="TypeBuilderHelper"/>.</returns>
		/// <seealso cref="System.Reflection.Emit.ModuleBuilder.DefineType(string,TypeAttributes,Type)">ModuleBuilder.DefineType Method</seealso>
		public TypeBuilderHelper DefineType(string name, Type parent)
		{
			return new TypeBuilderHelper(this, ModuleBuilder.DefineType(name, TypeAttributes.Public, parent));
		}

		/// <summary>
		/// Constructs a <see cref="TypeBuilderHelper"/> for a type with the specified name, its attributes, and base type.
		/// </summary>
		/// <param name="name">The full path of the type.</param>
		/// <param name="attrs">The attribute to be associated with the type.</param>
		/// <param name="parent">The Type that the defined type extends.</param>
		/// <returns>Returns the created <see cref="TypeBuilderHelper"/>.</returns>
		/// <seealso cref="System.Reflection.Emit.ModuleBuilder.DefineType(string,TypeAttributes,Type)">ModuleBuilder.DefineType Method</seealso>
		public TypeBuilderHelper DefineType(string name, TypeAttributes attrs, Type parent)
		{
			return new TypeBuilderHelper(this, ModuleBuilder.DefineType(name, attrs, parent));
		}

		/// <summary>
		/// Constructs a <see cref="TypeBuilderHelper"/> for a type with the specified name, base type,
		/// and the interfaces that the defined type implements.
		/// </summary>
		/// <param name="name">The full path of the type.</param>
		/// <param name="parent">The Type that the defined type extends.</param>
		/// <param name="interfaces">The list of interfaces that the type implements.</param>
		/// <returns>Returns the created <see cref="TypeBuilderHelper"/>.</returns>
		/// <seealso cref="System.Reflection.Emit.ModuleBuilder.DefineType(string,TypeAttributes,Type,Type[])">ModuleBuilder.DefineType Method</seealso>
		public TypeBuilderHelper DefineType(string name, Type parent, params Type[] interfaces)
		{
			return new TypeBuilderHelper(
				this,
				ModuleBuilder.DefineType(name, TypeAttributes.Public, parent, interfaces));
		}

		/// <summary>
		/// Constructs a <see cref="TypeBuilderHelper"/> for a type with the specified name, its attributes, base type,
		/// and the interfaces that the defined type implements.
		/// </summary>
		/// <param name="name">The full path of the type.</param>
		/// <param name="attrs">The attribute to be associated with the type.</param>
		/// <param name="parent">The Type that the defined type extends.</param>
		/// <param name="interfaces">The list of interfaces that the type implements.</param>
		/// <returns>Returns the created <see cref="TypeBuilderHelper"/>.</returns>
		/// <seealso cref="System.Reflection.Emit.ModuleBuilder.DefineType(string,TypeAttributes,Type,Type[])">ModuleBuilder.DefineType Method</seealso>
		public TypeBuilderHelper DefineType(string name, TypeAttributes attrs, Type parent, params Type[] interfaces)
		{
			return new TypeBuilderHelper(
				this,
				ModuleBuilder.DefineType(name, attrs, parent, interfaces));
		}

		#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 Fujitsu Canada
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions