#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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
}
}