Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Persistence/3.3/Core/DataMemberAccessor.cs @ 16713

Last change on this file since 16713 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

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