// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ReflectionExtensions.cs" company="Catel development team">
// Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
// Reflection extension class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Catel.Reflection
{
/// <summary>
/// Reflection extension class.
/// </summary>
public static class ReflectionExtensions
{
#region Variables
/// <summary>
/// Cache for the properties per type.
/// </summary>
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _properties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
/// <summary>
/// Cache for the methods per type.
/// </summary>
private static readonly Dictionary<Type, Dictionary<string, MethodInfo>> _methods = new Dictionary<Type, Dictionary<string, MethodInfo>>();
#endregion
#region Methods
/// <summary>
/// Gets the field info for a specific field. But, the good thing about this is that it also supports
/// <see cref="BindingFlags.FlattenHierarchy"/> for private members in base classes.
/// </summary>
/// <param name="type">The type to reflect.</param>
/// <param name="fieldName">Name of the field.</param>
/// <param name="bindingFlags">The binding flags.</param>
/// <returns><see cref="FieldInfo"/> or <c>null</c> if the field is not found.</returns>
public static FieldInfo GetFieldAndAllowPrivateBaseMembers(this Type type, string fieldName, BindingFlags bindingFlags)
{
FieldInfo fieldInfo = null;
bool flattenHierarchy = Enum<BindingFlags>.Flags.IsFlagSet(bindingFlags, BindingFlags.FlattenHierarchy);
if (flattenHierarchy)
{
bindingFlags = Enum<BindingFlags>.Flags.ClearFlag(bindingFlags, BindingFlags.FlattenHierarchy);
}
while (type != null)
{
fieldInfo = type.GetField(fieldName, bindingFlags);
if (flattenHierarchy)
{
if (fieldInfo != null)
{
break;
}
type = type.BaseType;
}
else
{
break;
}
}
return fieldInfo;
}
/// <summary>
/// Gets the property info for a specific property of a specific type.
/// </summary>
/// <param name="type">The type to reflect.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns><see cref="PropertyInfo"/> of the property or <c>null</c> if the property is not found.</returns>
public static PropertyInfo GetPropertyCached(this Type type, string propertyName)
{
var dictionary = _properties;
string memberName = propertyName;
var info = GetObjectFromCache(dictionary, type, memberName);
if (info == null)
{
var memberInfo = type.GetProperty(memberName);
AddObjectToCache(dictionary, type, memberName, memberInfo);
}
return GetObjectFromCache(dictionary, type, memberName);
}
/// <summary>
/// Gets the method for a specific type.
/// </summary>
/// <param name="type">The type that contains the member.</param>
/// <param name="methodName">Name of the method.</param>
/// <returns><see cref="MethodInfo"/> of the method or <c>null</c> if the method is not found.</returns>
public static MethodInfo GetMethodCached(this Type type, string methodName)
{
var dictionary = _methods;
string memberName = methodName;
var info = GetObjectFromCache(dictionary, type, memberName);
if (info == null)
{
var memberInfo = type.GetMethod(memberName);
AddObjectToCache(dictionary, type, memberName, memberInfo);
}
return GetObjectFromCache(dictionary, type, memberName);
}
/// <summary>
/// Adds an object to cache.
/// </summary>
/// <typeparam name="T">Type of the member.</typeparam>
/// <param name="dictionary">The dictionary.</param>
/// <param name="type">The type that contains the member.</param>
/// <param name="memberName">Name of the member.</param>
/// <param name="reflectedMember">The reflected member.</param>
private static void AddObjectToCache<T>(Dictionary<Type, Dictionary<string, T>> dictionary, Type type, string memberName, T reflectedMember)
{
if (!dictionary.ContainsKey(type))
{
dictionary.Add(type, new Dictionary<string, T>());
}
if (dictionary[type].ContainsKey(memberName))
{
dictionary[type][memberName] = reflectedMember;
}
else
{
dictionary[type].Add(memberName, reflectedMember);
}
}
/// <summary>
/// Gets an object from cache.
/// </summary>
/// <typeparam name="T">Type of the member.</typeparam>
/// <param name="dictionary">The dictionary.</param>
/// <param name="type">The type that contains the member.</param>
/// <param name="memberName">Name of the member.</param>
/// <returns>object or <c>null</c> if the object does not exist in the cache.</returns>
private static T GetObjectFromCache<T>(Dictionary<Type, Dictionary<string, T>> dictionary, Type type, string memberName) where T : class
{
if (!dictionary.ContainsKey(type))
{
dictionary.Add(type, new Dictionary<string, T>());
}
return dictionary[type].ContainsKey(memberName) ? dictionary[type][memberName] : null;
}
#endregion
}
}