Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.Persistence/3.3/Core/DataMemberAccessor.cs @ 15762

Last change on this file since 15762 was 12012, checked in by ascheibe, 10 years ago

#2212 merged r12008, r12009, r12010 back into trunk

File size: 9.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Reflection;
24using System.Reflection.Emit;
25
26namespace HeuristicLab.Persistence.Core {
27
28  /// <summary>
29  /// Encapsulation and abstraction for access a data member of an object
30  /// regardless of it being a property or field. Additionally a
31  /// default value and an alternate name can be specified.
32  /// </summary>
33  public sealed class DataMemberAccessor {
34
35    #region fields
36
37    /// <summary>
38    /// The function to get the value of the data member.
39    /// </summary>
40    public readonly Func<object, object> Get;
41
42    /// <summary>
43    /// The function to set the value of the data member.
44    /// </summary>
45    public readonly Action<object, object> Set;
46
47    /// <summary>
48    /// The name of the data member.
49    /// </summary>
50    public readonly string Name;
51
52    /// <summary>
53    /// The default value of the data member, can remain <c>null</c>
54    /// if no default value. If left null, this will also leave the
55    /// default value for value types (e.g. 0 for <c>int</c>).
56    /// </summary>
57    public readonly object DefaultValue;
58
59    #endregion
60
61    #region constructors
62
63    /// <summary>
64    /// Create a <see cref="DataMemberAccessor"/> from a FieldInfo or
65    /// PropertyInfo for the give object.
66    /// </summary>
67    /// <param name="memberInfo">The member info.</param>
68    /// <param name="name">The name.</param>
69    /// <param name="defaultvalue">The defaultvalue.</param>
70    public DataMemberAccessor(MemberInfo memberInfo, string name, object defaultvalue) {
71      Get = GenerateGetter(memberInfo);
72      Set = GenerateSetter(memberInfo);
73      Name = name;
74      DefaultValue = defaultvalue;
75    }
76
77    /// <summary>
78    /// Create an empty accessor that just encapsulates an object
79    /// without access.
80    /// </summary>   
81    public DataMemberAccessor() {
82      Name = null;
83      DefaultValue = null;
84      Get = Id;
85    }
86
87    /// <summary>
88    /// Create an empty accessor that just encapsulates an object
89    /// without access.
90    /// </summary>   
91    /// <param name="name">The object's name.</param>
92    public DataMemberAccessor(string name) {
93      Name = name;
94      DefaultValue = null;
95      Get = Id;
96    }
97
98    /// <summary>
99    /// Initializes a new instance of the <see cref="DataMemberAccessor"/> class using the
100    /// getter and setter from an exisiting instance but with a new name and default value.
101    /// </summary>
102    /// <param name="dma">The existing DataMemberAccessor.</param>
103    /// <param name="name">The new name.</param>
104    /// <param name="defaultValue">The new default value.</param>
105    public DataMemberAccessor(DataMemberAccessor dma, string name, object defaultValue) {
106      Get = dma.Get;
107      Set = dma.Set;
108      this.Name = name;
109      this.DefaultValue = defaultValue;
110    }
111
112    #endregion
113
114    #region auxiliary methods
115
116    /// <summary>
117    /// Returns a <see cref="System.String"/> that represents this instance.
118    /// </summary>
119    /// <returns>
120    /// A <see cref="System.String"/> that represents this instance.
121    /// </returns>
122    public override string ToString() {
123      return String.Format("DataMemberAccessor({0}, {1}, {2}, {3})",
124        Name,
125        DefaultValue ?? "<null>",
126        Get.Method, Set.Method);
127    }
128
129    /// <summary>
130    /// The identity function
131    /// </summary>
132    /// <param name="o">An object.</param>
133    /// <returns>its argument o unmodified.</returns>
134    public static object Id(object o) {
135      return o;
136    }
137
138    #endregion
139
140    #region static methods (code generators)
141
142    /// <summary>
143    /// Generate a getter for the given field or property
144    /// </summary>
145    /// <param name="memberInfo">The member info.</param>
146    /// <returns></returns>
147    public static Func<object, object> GenerateGetter(MemberInfo memberInfo) {
148      if (memberInfo.MemberType == MemberTypes.Field) {
149        FieldInfo fieldInfo = (FieldInfo)memberInfo;
150        return GenerateFieldGetter(fieldInfo);
151      } else if (memberInfo.MemberType == MemberTypes.Property) {
152        return GeneratePropertyGetter((PropertyInfo)memberInfo);
153      } else {
154        throw new PersistenceException(
155          "The Storable attribute can only be applied to fields and properties.");
156      }
157    }
158
159    /// <summary>
160    /// Generates a setter for the given field or property.
161    /// </summary>
162    /// <param name="memberInfo">The member info.</param>
163    /// <returns></returns>
164    public static Action<object, object> GenerateSetter(MemberInfo memberInfo) {
165      if (memberInfo.MemberType == MemberTypes.Field) {
166        FieldInfo fieldInfo = (FieldInfo)memberInfo;
167        return GenerateFieldSetter(fieldInfo);
168      } else if (memberInfo.MemberType == MemberTypes.Property) {
169        return GeneratePropertySetter((PropertyInfo)memberInfo);
170      } else {
171        throw new PersistenceException(
172          "The Storable attribute can only be applied to fields and properties.");
173      }
174    }
175
176    /// <summary>
177    /// Generates a dynamically compiled getter to access fields (even private ones).
178    /// </summary>
179    /// <param name="fieldInfo">The field info.</param>
180    /// <returns>A Func&lt;object, object&gt;</returns>
181    public static Func<object, object> GenerateFieldGetter(FieldInfo fieldInfo) {
182      DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, fieldInfo.DeclaringType, true);
183      ILGenerator ilgen = dm.GetILGenerator();
184      ilgen.Emit(OpCodes.Ldarg_0);
185      ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
186      ilgen.Emit(OpCodes.Ldfld, fieldInfo);
187      ilgen.Emit(OpCodes.Box, fieldInfo.FieldType);
188      ilgen.Emit(OpCodes.Ret);
189      return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>));
190    }
191
192    /// <summary>
193    /// Generates a dynamically compiled setter to access fields (even private ones).
194    /// </summary>
195    /// <param name="fieldInfo">The field info.</param>
196    /// <returns>An Action&lt;object, object%gt;</returns>
197    public static Action<object, object> GenerateFieldSetter(FieldInfo fieldInfo) {
198      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType, true);
199      ILGenerator ilgen = dm.GetILGenerator();
200      ilgen.Emit(OpCodes.Ldarg_0);
201      ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
202      ilgen.Emit(OpCodes.Ldarg_1);
203      ilgen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
204      ilgen.Emit(OpCodes.Stfld, fieldInfo);
205      ilgen.Emit(OpCodes.Ret);
206      return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
207    }
208
209    /// <summary>
210    /// Generates a dynamically compiled getter to access properties (even private ones).
211    /// </summary>
212    /// <param name="propertyInfo">The property info.</param>
213    /// <returns>A Func&lt;object, object&gt;</returns>
214    public static Func<object, object> GeneratePropertyGetter(PropertyInfo propertyInfo) {
215      MethodInfo getter = propertyInfo.GetGetMethod(true);
216      if (getter == null)
217        return null;
218      DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType, true);
219      ILGenerator ilgen = dm.GetILGenerator();
220      ilgen.Emit(OpCodes.Ldarg_0);
221      ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
222      ilgen.Emit(OpCodes.Callvirt, getter);
223      ilgen.Emit(OpCodes.Box, propertyInfo.PropertyType);
224      ilgen.Emit(OpCodes.Ret);
225      return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>));
226    }
227
228    /// <summary>
229    /// Generates a dynamically compiled setter to access properties (even private ones).
230    /// </summary>
231    /// <param name="propertyInfo">The property info.</param>
232    /// <returns>An Action&lt;object, object%gt;</returns>
233    public static Action<object, object> GeneratePropertySetter(PropertyInfo propertyInfo) {
234      MethodInfo setter = propertyInfo.GetSetMethod(true);
235      if (setter == null)
236        return null;
237      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType, true);
238      ILGenerator ilgen = dm.GetILGenerator();
239      ilgen.Emit(OpCodes.Ldarg_0);
240      ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
241      ilgen.Emit(OpCodes.Ldarg_1);
242      ilgen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
243      ilgen.Emit(OpCodes.Callvirt, setter);
244      ilgen.Emit(OpCodes.Ret);
245      return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
246    }
247
248    #endregion
249  }
250
251}
Note: See TracBrowser for help on using the repository browser.