Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3962 was 3913, checked in by epitzer, 15 years ago

Accelerate persistence: (#646)

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