#region License Information /* HeuristicLab * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Reflection; using System.Reflection.Emit; namespace HeuristicLab.Persistence.Core { /// /// Encapsulation and abstraction for access a data member of an object /// regardless of it being a property or field. Additionally a /// default value and an alternate name can be specified. /// public sealed class DataMemberAccessor { #region fields /// /// The function to get the value of the data member. /// public readonly Func Get; /// /// The function to set the value of the data member. /// public readonly Action Set; /// /// The name of the data member. /// public readonly string Name; /// /// The default value of the data member, can remain null /// if no default value. If left null, this will also leave the /// default value for value types (e.g. 0 for int). /// public readonly object DefaultValue; #endregion #region constructors /// /// Create a from a FieldInfo or /// PropertyInfo for the give object. /// /// The member info. /// The name. /// The defaultvalue. public DataMemberAccessor(MemberInfo memberInfo, string name, object defaultvalue) { Get = GenerateGetter(memberInfo); Set = GenerateSetter(memberInfo); Name = name; DefaultValue = defaultvalue; } /// /// Create an empty accessor that just encapsulates an object /// without access. /// public DataMemberAccessor() { Name = null; DefaultValue = null; Get = Id; } /// /// Create an empty accessor that just encapsulates an object /// without access. /// /// The object's name. public DataMemberAccessor(string name) { Name = name; DefaultValue = null; Get = Id; } /// /// Initializes a new instance of the class using the /// getter and setter from an exisiting instance but with a new name and default value. /// /// The existing DataMemberAccessor. /// The new name. /// The new default value. public DataMemberAccessor(DataMemberAccessor dma, string name, object defaultValue) { Get = dma.Get; Set = dma.Set; this.Name = name; this.DefaultValue = defaultValue; } #endregion #region auxiliary methods /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return String.Format("DataMemberAccessor({0}, {1}, {2}, {3})", Name, DefaultValue ?? "", Get.Method, Set.Method); } /// /// The identity function /// /// An object. /// its argument o unmodified. public static object Id(object o) { return o; } #endregion #region static methods (code generators) /// /// Generate a getter for the given field or property /// /// The member info. /// public static Func GenerateGetter(MemberInfo memberInfo) { if (memberInfo.MemberType == MemberTypes.Field) { FieldInfo fieldInfo = (FieldInfo)memberInfo; return GenerateFieldGetter(fieldInfo); } else if (memberInfo.MemberType == MemberTypes.Property) { return GeneratePropertyGetter((PropertyInfo)memberInfo); } else { throw new PersistenceException( "The Storable attribute can only be applied to fields and properties."); } } /// /// Generates a setter for the given field or property. /// /// The member info. /// public static Action GenerateSetter(MemberInfo memberInfo) { if (memberInfo.MemberType == MemberTypes.Field) { FieldInfo fieldInfo = (FieldInfo)memberInfo; return GenerateFieldSetter(fieldInfo); } else if (memberInfo.MemberType == MemberTypes.Property) { return GeneratePropertySetter((PropertyInfo)memberInfo); } else { throw new PersistenceException( "The Storable attribute can only be applied to fields and properties."); } } /// /// Generates a dynamically compiled getter to access fields (even private ones). /// /// The field info. /// A Func<object, object> public static Func GenerateFieldGetter(FieldInfo fieldInfo) { DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, fieldInfo.DeclaringType, true); ILGenerator ilgen = dm.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); ilgen.Emit(OpCodes.Ldfld, fieldInfo); ilgen.Emit(OpCodes.Box, fieldInfo.FieldType); ilgen.Emit(OpCodes.Ret); return (Func)dm.CreateDelegate(typeof(Func)); } /// /// Generates a dynamically compiled setter to access fields (even private ones). /// /// The field info. /// An Action<object, object%gt; public static Action GenerateFieldSetter(FieldInfo fieldInfo) { DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType, true); ILGenerator ilgen = dm.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType); ilgen.Emit(OpCodes.Ldarg_1); ilgen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); ilgen.Emit(OpCodes.Stfld, fieldInfo); ilgen.Emit(OpCodes.Ret); return (Action)dm.CreateDelegate(typeof(Action)); } /// /// Generates a dynamically compiled getter to access properties (even private ones). /// /// The property info. /// A Func<object, object> public static Func GeneratePropertyGetter(PropertyInfo propertyInfo) { MethodInfo getter = propertyInfo.GetGetMethod(true); if (getter == null) return null; DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType, true); ILGenerator ilgen = dm.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); ilgen.Emit(OpCodes.Callvirt, getter); ilgen.Emit(OpCodes.Box, propertyInfo.PropertyType); ilgen.Emit(OpCodes.Ret); return (Func)dm.CreateDelegate(typeof(Func)); } /// /// Generates a dynamically compiled setter to access properties (even private ones). /// /// The property info. /// An Action<object, object%gt; public static Action GeneratePropertySetter(PropertyInfo propertyInfo) { MethodInfo setter = propertyInfo.GetSetMethod(true); if (setter == null) return null; DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType, true); ILGenerator ilgen = dm.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); ilgen.Emit(OpCodes.Ldarg_1); ilgen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); ilgen.Emit(OpCodes.Callvirt, setter); ilgen.Emit(OpCodes.Ret); return (Action)dm.CreateDelegate(typeof(Action)); } #endregion } }