Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Catel - Part 4 of n: Unit testing with Catel

, 28 Jan 2011
This article explains how to write unit tests for MVVM using Catel.
Catel-04_01-unittesting.zip
src
Catel.Articles.04 - Unit testing
Models
Properties
Settings.settings
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
ViewModels
Windows
Catel.Articles.04 - Unit testing.Test
Models
Properties
UI
ViewModels
Catel.Articles.Base
Data
Attributes
Properties
Settings.settings
Resources
Images
CatenaLogic.png
Preview.png
Run.png
ShowCode.png
UI
Controls
Helpers
ViewModels
Windows
Catel.Core
Attributes
ClassDiagrams
DataObjectBase.cd
SavableDataObjectBase.cd
Collections
Helpers
ComponentModel
Data
Attributes
Exceptions
Interfaces
Diagnostics
Extensions
Helpers
Exceptions
Helpers
IO
Exceptions
IoC
LLBLGen
Log4net
Appender
Extensions
Helpers
MVVM
Commands
Interfaces
Exceptions
Services
EventArgs
Exceptions
Interfaces
ViewModels
Attributes
Interfaces
Properties
Reflection
Exceptions
Extensions
Helpers
Runtime
Serialization
Attributes
Helpers
Security
Cryptography
Helpers
Catel.Examples.Models
Properties
Catel.Examples.PersonApplication
Properties
Settings.settings
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
ViewModels
Windows
Catel.Examples.Silverlight
Properties
Resources
Images
add.png
delete.png
edit.png
group.png
UI
Data
Converters
Pages
ViewModels
Windows
Catel.Examples.Silverlight.Web
Catel.Examples.Silverlight.Web.csproj.user
ClientBin
Properties
Catel.FxCop
Catel.Silverlight
Diagnostics
log4net
Core
MVVM
Commands
Services
ViewModels
Properties
Catel.Core
Catel.Windows
Reflection
Themes
Generic
Assets
Old
Windows
Controls
Data
Converters
Helpers
Helpers
Catel.Silverlight.Test
Properties
Catel.Silverlight.Test.Web
Catel.Silverlight.Test.Web.csproj.user
ClientBin
Properties
Catel.snk
Catel.Templates.WpfApplication
Properties
Settings.settings
UI
Controls
ViewModels
Windows
Catel.Templates.WpfItemTemplates
Properties
UI
Controls
ViewModels
Windows
Catel.Test
Collections
Convert
Data
Helpers
IO
MVVM
UI
ViewModels
Properties
Reflection
Runtime
Serialization
Security
Cryptography
Test References
Catel.Windows.accessor
Windows
Data
Converters
Catel.vsmdi
Catel.Windows
ClassDiagrams
ViewModelBase.cd
Collections
Extensions
Helpers
MVVM
Commands
Services
Test
UI
ViewModels
Properties
Settings.settings
Resources
Images
Add.png
ClearOutput.png
Edit.png
Error.png
Loading.gif
Preview.png
Remove.png
Save.png
TipOfTheDay.png
Warning.png
Themes
Aero
ExpressionDark
Assets
Generic
Assets
Controls
Jetpack
Assets
background.png
Old
SunnyOrange
Assets
Windows
Controls
Extensions
LinkLabel
StackGrid
Data
Converters
Helpers
Documents
Extensions
Extensions
Helpers
Input
Markup
Media
Effects
EmptyEffect
EmptyEffect.fx
EmptyEffect.ps
EmptyEffect.fx
GrayscaleEffect
GrayscaleEffect.fx
GrayscaleEffect.ps
Extensions
Imaging
Extensions
Windows
DataWindow
TipOfTheDay
Local.testsettings
Settings.StyleCop
TraceAndTestImpact.testsettings
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TypeHelper.cs" company="Catel development team">
//   Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
//   <see cref="Type" /> helper class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Catel.Properties;
using log4net;

#if SILVERLIGHT
using Catel.Reflection;
#endif

namespace Catel
{
    /// <summary>
    /// <see cref="Type"/> helper class.
    /// </summary>
    public static class TypeHelper
    {
        #region Variables
        /// <summary>
        /// The <see cref="ILog">log</see> object.
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        #endregion

        #region Methods
        /// <summary>
        /// Determines whether the subclass is of a raw generic type.
        /// </summary>
        /// <param name="generic">The generic.</param>
        /// <param name="toCheck">The type to check.</param>
        /// <returns>
        /// 	<c>true</c> if the subclass is of a raw generic type; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="ArgumentNullException">when <paramref name="generic"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">when <paramref name="toCheck"/> is <c>null</c>.</exception>
        /// <remarks>
        /// This implementation is based on this forum thread:
        /// http://stackoverflow.com/questions/457676/c-reflection-check-if-a-class-is-derived-from-a-generic-class
        /// </remarks>
        public static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
        {
            if (generic == null)
            {
            	throw new ArgumentNullException("generic");
            }

            if (toCheck == null)
            {
            	throw new ArgumentNullException("toCheck");
            }

            while ((toCheck != null) && (toCheck != typeof(object)))
            {
                var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
                if (generic == cur)
                {
                	return true;
                }

                toCheck = toCheck.BaseType;
            }

            return false;
        }

        /// <summary>
        /// Formats a type in the official type description like [typename], [assemblyname].
        /// </summary>
        /// <param name="assembly">Assembly name to format.</param>
        /// <param name="type">Type name to format.</param>
        /// <returns>Type name like [typename], [assemblyname].</returns>
        public static string FormatType(string assembly, string type)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", type, assembly);
        }

        /// <summary>
        /// Formats multiple inner types into one string.
        /// </summary>
        /// <param name="innerTypes">The inner types.</param>
        /// <returns>
        /// string representing a combination of all inner types.
        /// </returns>
        public static string FormatInnerTypes(string[] innerTypes)
        {
            string result = string.Empty;

            for (int i = 0; i < innerTypes.Length; i++)
            {
                result += string.Format(CultureInfo.InvariantCulture, "[{0}]", innerTypes[i]);

                // Postfix a comma if this is not the last
                if (i < innerTypes.Length - 1)
                {
                	result += ",";
                }
            }

            return result;
        }

        /// <summary>
        /// Returns whether a type is nullable or not.
        /// </summary>
        /// <param name="type">Type to check.</param>
        /// <returns>
        /// True if the type is nullable, otherwise false.
        /// </returns>
        public static bool IsTypeNullable(Type type)
        {
            if (type == null)
            {
            	return false;
            }

            if (!type.IsValueType)
            {
            	return true;
            }

            if (Nullable.GetUnderlyingType(type) != null)
            {
            	return true;
            }

            return false;
        }

        /// <summary>
        /// Checks whether the 2 specified objects are equal. This method is better, simple because it also checks boxing so
        /// 2 integers with the same values that are boxed are equal.
        /// </summary>
        /// <param name="object1">The first object.</param>
        /// <param name="object2">The second object.</param>
        /// <returns><c>true</c> if the objects are equal; otherwise <c>false</c>.</returns>
        public static bool AreObjectsEqual(object object1, object object2)
        {
            if ((object1 == null) && (object2 == null))
            {
            	return true;
            }

            if ((object1 == null) || (object2 == null))
            {
            	return false;
            }

            return object1.Equals(object2);
        }

        /// <summary>
        /// Gets the <see cref="PropertyInfo"/> for the specified property.
        /// </summary>
        /// <param name="type">The type that contains the properties.</param>
        /// <param name="property">The property name.</param>
        /// <returns>
        /// 	<see cref="PropertyInfo"/> or <c>null</c> if no property info is found.
        /// </returns>
        public static PropertyInfo GetPropertyInfo(Type type, string property)
        {
            PropertyInfo propertyInfo = type.GetProperty(property, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo == null)
            {
            	propertyInfo = type.GetProperty(property, BindingFlags.NonPublic | BindingFlags.Instance);
            }

            if (propertyInfo == null)
            {
                Log.Warn(TraceMessages.CannotFindProperty, property);
            }

            return propertyInfo;
        }
        #endregion

        #region [ object size ]

        /// <summary>
        /// Cache of type sizes.
        /// </summary>
        private static readonly IDictionary<Type, int> TypeSizes = InitializeSizeHelper();

        /// <summary>
        /// Initializes the size helper.
        /// </summary>
        /// <returns>Dictionary containing the type and its size.</returns>
        private static IDictionary<Type, int> InitializeSizeHelper()
        {
            IDictionary<Type, int> rv = new Dictionary<Type, int>();

            rv.Add(typeof(DateTime), 8);
            rv.Add(typeof(Pointer), 8); // Prevent endless loops.
            rv.Add(typeof(long), 8);
            rv.Add(typeof(int), 4);
            rv.Add(typeof(short), 2);
            rv.Add(typeof(ulong), 8);
            rv.Add(typeof(uint), 4);
            rv.Add(typeof(ushort), 2);

            return rv;
        }

        /// <summary>
        /// Determine the size of any object.
        /// </summary>
        /// <param name="obj">The object you want to know the size of.</param>
        /// <returns>A size in bytes, or -1 if the request failed.</returns>
        public static int SizeOf(object obj)
        {
            int sizeValue;

            try
            {
                sizeValue = ReflectiveSizeOf(obj);
            }
            catch
            {
                sizeValue = -1;
            }

            return sizeValue;
        }

        /// <summary>
        /// Determines the reflective size of any object.
        /// </summary>
        /// <param name="obj">The object you want to know the size of.</param>
        /// <returns>A size in bytes, or -1 if the request failed.</returns>
        private static int ReflectiveSizeOf(object obj)
        {
            int size = 0;

            if (obj != null)
            {
                Queue<object> objects = new Queue<object>();
                Queue<object> done = new Queue<object>();

                objects.Enqueue(obj);

                while (objects.Count > 0)
                {
                    object target = objects.Dequeue();
                    int targetSize = -1;

                    done.Enqueue(target);

                    if (target == null)
                    {
                        targetSize = 0;
                    }
                    else
                    {
                        try
                        {
                            Type targetType = target.GetType();

                            if (targetType.IsEnum)
                            {
                                targetType = Enum.GetUnderlyingType(targetType);
                            }

                            if (TypeSizes.ContainsKey(targetType))
                            {
                                if (targetSize == -1)
                                {
                                    targetSize = 0;
                                }

                                targetSize += TypeSizes[targetType];
                            }
                            else
                            {
                                if (targetType.IsValueType)
                                {
                                    // int ts2 = System.Runtime.InteropServices.Marshal.SizeOf(targetType);
                                    targetSize = System.Runtime.InteropServices.Marshal.SizeOf(target);
                                }
                                else
                                {
                                    if (target is Delegate)
                                    {
                                        targetSize += TypeSizes[typeof(Pointer)];
                                    }
                                    else
                                    {
                                        targetSize = -2;
                                    }
                                }
                            }
                        }
                        catch (Exception)
                        {
                            targetSize = -2;
                        }
                    }

                    if (target != null && targetSize < 0)
                    {
                        Type targetType = target.GetType();
                        FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.GetField | BindingFlags.Public | BindingFlags.NonPublic);

                        foreach (FieldInfo f in fields)
                        {
                            object fieldValue = f.GetValue(target);

                            if (fieldValue != null && !objects.Contains(fieldValue) && !done.Contains(fieldValue))
                            {
                                objects.Enqueue(fieldValue);
                            }
                        }

                        if (target is IEnumerable)
                        {
                            if (target is string)
                            {
                                string targetAsString = target as string;
                                size += targetAsString.Length;
                            }
                            else
                            {
                                IEnumerable col = target as IEnumerable;

                                foreach (object c in col)
                                {
                                    if (c != null && !objects.Contains(c) && !done.Contains(c))
                                    {
                                        objects.Enqueue(c);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        size += targetSize;
                    }
                }

                objects.Clear();	// Should already be empty.
                done.Clear();
            }

            return size;
        }

        #endregion

        #region TryParseEx

        /// <summary>
        /// Helper class for converting Strings to GUIDs.
        /// </summary>
        public static class Parser
        {
            /// <summary>
            /// Pattern guid tester.
            /// </summary>
            private const string PatternGuidTester = "^[0-9A-F]{8}(-?)[0-9A-F]{4}\\1[0-9A-F]{4}\\1[0-9A-F]{4}\\1[0-9A-F]{12}$";

            /// <summary>
            /// The <see cref="Regex"/> containing the guid tester.
            /// </summary>
            private static readonly Regex RxGuidTester = new Regex(PatternGuidTester, RegexOptions.IgnoreCase | RegexOptions.Singleline);

            /// <summary>
            /// Tries to parse a string to a guid.
            /// </summary>
            /// <param name="input">The string version of the guid. Currently supported layouts dddddddd-dddd-dddd-dddd-dddddddddddd and dddddddddddddddddddddddddddddddd where d is a hexadecimal digit.</param>
            /// <param name="output">When the parse succeeds this is the guid else it is set to Guid.Empty.</param>
            /// <returns>False when the parse failed else true.</returns>
            public static bool TryParseGuid(string input, out Guid output)
            {
                bool hasFailed = string.IsNullOrEmpty(input) || !RxGuidTester.IsMatch(input);

                output = !hasFailed ? new Guid(input) : Guid.Empty;

                return !hasFailed;
            }
        }

        #endregion TryParseEx

        #region Powercast
        #region Typed input and output

        /// <summary>
        /// Tries to Generic cast of a value.
        /// </summary>
        /// <typeparam name="TOutput">Requested return type.</typeparam>
        /// <typeparam name="TInput">The input type.</typeparam>
        /// <param name="value">The value to cast.</param>
        /// <param name="output">The casted value.</param>
        /// <returns>When a cast is succeded true else false.</returns>
        public static bool TryCast<TOutput, TInput>(TInput value, out TOutput output)
        {
            bool success = true;
            Type outputType = typeof(TOutput);
            Type innerType = Nullable.GetUnderlyingType(outputType);

            // Database support...
            if (value == null || Convert.IsDBNull(value))
            {
                output = default(TOutput);

                if (outputType.IsValueType && innerType == null)
                {
                    success = false;
                }
                else
                {
                    // Non-valuetype can contain nill.
                    // (Nullable<T> also)
                    success = true;
                }
            }
            else
            {
                Type inputType = value.GetType();

                if (inputType.IsAssignableFrom(outputType))
                {
                    // Direct assignable
                    success = true; 
                    output = (TOutput)(object)value;
                }
                else
                {
                    output = (TOutput)Convert.ChangeType(value, innerType ?? outputType, CultureInfo.InvariantCulture);
                    success = true;
                }
            }

            return success;
        }

        /// <summary>
        /// Generic cast of a value.
        /// </summary>
        /// <typeparam name="TOutput">Requested return type.</typeparam>
        /// <typeparam name="TInput">The input type.</typeparam>
        /// <param name="value">The value to cast.</param>
        /// <returns>The casted value.</returns>
        public static TOutput Cast<TOutput, TInput>(TInput value)
        {
            TOutput output = default(TOutput);

            if (!TryCast(value, out output))
            {
                string tI = typeof(TInput).FullName;
                string tO = typeof(TOutput).FullName;
                string vl = string.Concat(value);
                string msg = "Failed to cast from '{0}' to '{1}'";

                if (!tI.Equals(vl))
                {
                    if (value == null)
                    {
                        vl = "nill-value";
                    }

                    msg = string.Concat(msg, " for value '{2}'");
                }

                msg = string.Format(msg, tI, tO, vl);
                throw new InvalidCastException(msg);
            }

            return output;
        }

        /// <summary>
        /// Generic cast of a value.
        /// </summary>
        /// <typeparam name="TOutput">Requested return type.</typeparam>
        /// <typeparam name="TInput">The input type.</typeparam>
        /// <param name="value">The value to cast.</param>
        /// <param name="whenNullValue">When unable to cast the incoming value, this value is returned instead.</param>
        /// <returns>The casted value or when uncastable the <paramref name="whenNullValue" /> is returned.</returns>
        public static TOutput Cast<TOutput, TInput>(TInput value, TOutput whenNullValue)
        {
            TOutput output;

            if (!TryCast(value, out output) || output == null)
            {
                output = whenNullValue;
            }

            return output;
        }

        #endregion
        #region Unknown input type / common object input

        /// <summary>
        /// Generic cast of a value.
        /// </summary>
        /// <typeparam name="TOutput">Requested return type.</typeparam>
        /// <param name="input">The object to cast.</param>
        /// <returns>The casted value.</returns>
        public static TOutput Cast<TOutput>(object input)
        {
            return Cast<TOutput, object>(input);
        }

        /// <summary>
        /// Generic cast of a value.
        /// </summary>
        /// <typeparam name="TOutput">Requested return type.</typeparam>
        /// <param name="input">The input.</param>
        /// <param name="whenNullValue">When unable to cast the incoming value, this value is returned instead.</param>
        /// <returns>
        /// The casted value or when uncastable the <paramref name="whenNullValue"/> is returned.
        /// </returns>
        public static TOutput Cast<TOutput>(object input, TOutput whenNullValue)
        {
            return Cast<TOutput, object>(input, whenNullValue);
        }

        #endregion
        #endregion

        #region Reflection helpers

        /// <summary>
        /// Gets a specific property from an object.
        /// </summary>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <typeparam name="TObject">The type of the object.</typeparam>
        /// <param name="property">The name of the property to get.</param>
        /// <param name="sourceObject">The source object.</param>
        /// <returns>The property.</returns>
        public static TProperty PropertyGet<TProperty, TObject>(string property, TObject sourceObject)
        {
            // Use cache?
            if (string.IsNullOrEmpty(property))
            {
                throw new ArgumentNullException("property", "Parameter is Null or Empty.");
            }

            if (sourceObject == null)
            {
                throw new ArgumentNullException("sourceObject");
            }

            Type objType = typeof(TObject);
            PropertyInfo pi = objType.GetProperty(property, typeof(TProperty), Type.EmptyTypes);

            if (pi == null || !pi.CanRead)
            {
                string msg;

                if (pi == null)
                {
                    msg = "No public {2}-property found with the name '{0}' in type '{1}'";
                }
                else
                {
                    msg = "{2}-property '{0}' in type '{1}' cannot be read.";
                }

                msg = string.Format(msg, property, objType.FullName, typeof(TProperty).Name);

                throw new Exception(msg);
            }

            MethodInfo mi = pi.GetGetMethod();
            object value = mi.Invoke(sourceObject, new object[0]);

            return Cast<TProperty>(value);
        }

        /// <summary>
        /// Gets the property value.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="propertyPath">The property path.</param>
        /// <returns>The property value.</returns>
        public static object GetPropertyValue(object instance, string propertyPath)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }

            if (string.IsNullOrEmpty(propertyPath))
            {
                throw new ArgumentNullException("propertyPath");
            }

            return GetMemberValue(instance.GetType(), instance, propertyPath);
        }

        /// <summary>
        /// This method gives you the ability to read values of private fields of an object.
        /// </summary>
        /// <param name="instance">The instance of the object of which you want to know the private value.</param>
        /// <param name="fieldName">The name of the field you want to know... when you want to know a field of an private object of an object you can use the common OO-notation.</param>
        /// <returns>
        /// The value of the object.
        /// <para />
        /// NOTE: When the Field is not found the object, an exception is thrown.
        /// </returns>
        public static object GetField(object instance, string fieldName)
        {
            Type instanceType = instance.GetType();

            return GetField(instanceType, instance, fieldName);
        }

        /// <summary>
        /// This method gives you the ability to read values of private fields of an object.
        /// </summary>
        /// <param name="typeInterface">The interface use want to use when interpeting the class</param>
        /// <param name="instance">The instance of the object of which you want to know the private value.</param>
        /// <param name="fieldName">The name of the field you want to know... when you want to know a field of an private object of an object you can use the common OO-notation.</param>
        /// <returns>
        /// The value of the object.
        /// <para />
        /// NOTE: When the Field is not found the object, an exception is thrown.
        /// </returns>
        public static object GetField(Type typeInterface, object instance, string fieldName)
        {
            // TODO: Use GetMemberInfo
            int firstIndexOfPeriod = fieldName.IndexOf('.');
            string firstPart = (firstIndexOfPeriod == -1) ? fieldName : fieldName.Substring(0, firstIndexOfPeriod);
            string restPart = (firstIndexOfPeriod == -1) ? null : fieldName.Substring(firstIndexOfPeriod + 1);
            object fieldValue;
            FieldInfo fieldDesc = typeInterface.GetField(firstPart, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField);

            if (fieldDesc == null)
            {
                if (typeInterface.BaseType == null)
                {
                    // TODO: Use an dedicated exception-class
                    throw new Exception("Field not found in object-type (or any base-type).");
                }

                fieldValue = GetField(typeInterface.BaseType, instance, fieldName);
            }
            else
            {
                fieldValue = fieldDesc.GetValue(instance);

                if (restPart != null)
                {
                    fieldValue = GetField(fieldValue, restPart);
                }
            }

            return fieldValue;
        }

        /// <summary>
        /// Gets the member info.
        /// </summary>
        /// <param name="typeInterface">The type interface.</param>
        /// <param name="instance">The instance.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <returns>The value of the object.</returns>
        private static object GetMemberValue(Type typeInterface, object instance, string fieldName)
        {
            // TODO: Helaas klopt er nu geen r(u)(ee)(k)(t) van....
            // TODO: Validate typeFlag
            int firstIndexOfPeriod = fieldName.IndexOf('.');
            string firstPart = (firstIndexOfPeriod == -1) ? fieldName : fieldName.Substring(0, firstIndexOfPeriod);
            string restPart = (firstIndexOfPeriod == -1) ? null : fieldName.Substring(firstIndexOfPeriod + 1);
            MemberInfo[] memberDesc = typeInterface.GetMember(firstPart, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            object memberValue;

            if (memberDesc == null || memberDesc.Length == 0)
            {
                if (typeInterface.BaseType == null)
                {
                    // TODO: Use an dedicated exception-class
                    throw new Exception("Field not found in object-type (or any base-type).");
                }

                memberValue = GetMemberValue(typeInterface.BaseType, instance, fieldName);
            }
            else
            {
                if (memberDesc.Length > 1)
                {
                    throw new NotSupportedException("Too many results.");
                }

                MemberInfo mi = memberDesc[0];

                if (mi is FieldInfo)
                {
                    FieldInfo fi = (FieldInfo)mi;
                    memberValue = fi.GetValue(instance);
                }
                else if (mi is PropertyInfo)
                {
                    PropertyInfo pi = (PropertyInfo)mi;
                    memberValue = pi.GetValue(instance, null);
                }
                else
                {
                    // Currently not supported.
                    throw new NotSupportedException();
                }

                if (restPart != null && memberValue != null)
                {
                    memberValue = GetMemberValue(memberValue.GetType(), memberValue, restPart);
                }
            }

            return memberValue;
        }

        /// <summary>
        /// This method gives you the ability to change values of private fields of an object.
        /// </summary>
        /// <param name="instance">The instance of the object of which you want to change a field-value.</param>
        /// <param name="fieldName">The field-name... when you want to know a field of an private object of an object you can use the common OO-notation.</param>
        /// <param name="value">The value you want to put in the field, WARNING this method does NOT check the types of the objects.</param>
        public static void SetField(object instance, string fieldName, object value)
        {
            Type instanceType = instance.GetType();

            SetField(instanceType, instance, fieldName, value);
        }

        /// <summary>
        /// This method gives you the ability to change values of private fields of an object.
        /// </summary>
        /// <param name="typeInterface">The interface use want to use when interpeting the class</param>
        /// <param name="instance">The instance of the object of which you want to change a field-value.</param>
        /// <param name="fieldName">The field-name... when you want to know a field of an private object of an object you can use the common OO-notation.</param>
        /// <param name="value">The value you want to put in the field, WARNING this method does NOT check the types of the objects.</param>
        public static void SetField(Type typeInterface, object instance, string fieldName, object value)
        {
            int firstIndexOfPeriod = fieldName.IndexOf('.');
            string firstPart = (firstIndexOfPeriod == -1) ? fieldName : fieldName.Substring(0, firstIndexOfPeriod);
            string restPart = (firstIndexOfPeriod == -1) ? null : fieldName.Substring(firstIndexOfPeriod + 1);
            FieldInfo fieldDesc = typeInterface.GetField(firstPart, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField);

            if (fieldDesc == null)
            {
                if (typeInterface.BaseType == null)
                {
                    throw new Exception("Field not found in object-type (or any base-type).");
                }

                SetField(typeInterface.BaseType, instance, fieldName, value);
            }
            else
            {
                if (restPart != null)
                {
                    object fieldValue = fieldDesc.GetValue(instance);
                    SetField(fieldValue, restPart, value);

                    if (fieldValue is ValueType)
                    {
                        fieldDesc.SetValue(instance, fieldValue);
                    }
                }
                else
                {
                    fieldDesc.SetValue(instance, value);
                }
            }
        }

        #endregion

        #region Hash-tool

        /// <summary>
        /// Combine multiple hashcodes in to one.
        /// </summary>
        /// <param name="hashes">An array of hashcodes.</param>
        /// <returns>An 'unique' hashcode.</returns>
        /// <remarks>
        /// Based on System.Web.UI.HashCodeCombiner (use Reflector).
        /// </remarks>
        public static int CombineHash(params int[] hashes)
        {
            if (hashes == null || hashes.Length == 0)
            {
                throw new ArgumentException("Array can't be null or empty.", "hashes");
            }

            int hash = 5381; // 0x1505L

            foreach (int inp in hashes)
            {
                hash = ((hash << 5) + hash) ^ inp;
            }

            // Make sure the hash is not negative
            if (hash < 0)
            {
                hash = hash * -1;
            }

            // Return hash
            return hash;
        }

        #endregion

        #region Dispose helper function
        /// <summary>
        /// Clears all events.
        /// </summary>
        /// <param name="obj">The object.</param>
        public static void ClearAllEvents(object obj)
        {
            // Geen object, geen events.
            if (obj == null)
            {
            	return;
            }

            Type objectType = obj.GetType();

            EventInfo[] eis = objectType.GetEvents();

            // Ga alle events van dit object af.
            foreach (EventInfo ei in eis)
            {
                UnsubscribeAll(obj, ei);
            }
        }

        /// <summary>
        /// Unsubscribes all.
        /// </summary>
        /// <param name="obj">The object.</param>
        /// <param name="ei">The event info.</param>
        private static void UnsubscribeAll(object obj, EventInfo ei)
        {
            if (ei.EventHandlerType.BaseType != typeof(MulticastDelegate))
            {
                // Geen MulticastDelegate => geen event.
                return;
            }

            MethodInfo invoke = ei.EventHandlerType.GetMethod("Invoke");
            if (invoke == null)
            {
                // Geen Invoke-method => geen event.
                return;
            }

            Type objectType = obj.GetType();
            FieldInfo eventField = objectType.GetField(ei.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (eventField == null)
            {
            	return;
            }

            // Value van dit Event-field, de feitelijke storage van de delegates.
            object eventValue = eventField.GetValue(obj);
            if (eventValue == null)
            {
            	return;
            }

            // Geregistreerde delegates ophalen.
            MethodInfo getInvocationList = ei.EventHandlerType.GetMethod("GetInvocationList");
            Delegate[] delegates = (Delegate[])getInvocationList.Invoke(eventValue, null);

            // Elke delegate uit het event verwijderen.
            foreach (Delegate handler in delegates)
            {
                ei.RemoveEventHandler(obj, handler);
            }
        }

        /// <summary>
        /// Clears the event.
        /// </summary>
        /// <param name="instance">The object instance.</param>
        /// <param name="eventHandler">The event handler.</param>
        public static void ClearEvent(object instance, object eventHandler)
        {
            if (instance == null)
            {
            	return;
            }

            if (eventHandler == null)
            {
            	return;
            }

            Type etype = eventHandler.GetType();
            if (etype.BaseType != typeof(MulticastDelegate))
            {
                // Geen MulticastDelegate => geen event.
                return;
            }

            MethodInfo invoke = etype.GetMethod("Invoke");
            if (invoke == null)
            {
                // Geen Invoke-method => geen event.
                return;
            }

            const BindingFlags AllInstance = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            Type instanceType = instance.GetType();

            // Event info zoeken bij de geleverde event-storage
            EventInfo eventInfo = (from fi in instanceType.GetFields(AllInstance)
                                   let fieldvalue = fi.GetValue(instance)
                                   where fieldvalue == eventHandler
                                   select instanceType.GetEvent(fi.Name, AllInstance)).FirstOrDefault();
            if (eventInfo == null)
            {
            	return;
            }

            // Geregistreerde delegates ophalen.
            MethodInfo getInvocationList = etype.GetMethod("GetInvocationList");
            Delegate[] delegates = (Delegate[])getInvocationList.Invoke(eventHandler, null);

            // Elke delegate uit het event verwijderen.
            foreach (Delegate handler in delegates)
            {
                eventInfo.RemoveEventHandler(instance, handler);
            }
        }
        #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)

Share

About the Author

Geert van Horrik
Software Developer CatenaLogic
Netherlands Netherlands
I am Geert van Horrik, and I have studied Computer Science in the Netherlands.
 
I love to write software using .NET (especially the combination of WPF and C#). I am also the lead developer of Catel, an open-source application development framework for WPF, Silverlight, WP7 and WinRT with the focus on MVVM.
 
I have my own company since January 1st 2007, called CatenaLogic. This company develops commercial and non-commercial software.
 
To download (or buy) applications I have written, visit my website: http://www.catenalogic.com
Follow on   Twitter

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 28 Jan 2011
Article Copyright 2011 by Geert van Horrik
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid