comparison Source/TypeBuilder/Builders/PropertyChangedBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Reflection;
3 using System.Reflection.Emit;
4 using BLToolkit.Reflection;
5 using BLToolkit.Reflection.Emit;
6
7 namespace BLToolkit.TypeBuilder.Builders
8 {
9 public class PropertyChangedBuilder : AbstractTypeBuilderBase
10 {
11 public PropertyChangedBuilder()
12 : this(Common.Configuration.NotifyOnEqualSet, true, true)
13 {
14 }
15
16 public PropertyChangedBuilder(bool notifyOnEqualSet, bool useReferenceEquals, bool skipSetterOnNoChange)
17 {
18 _notifyOnEqualSet = notifyOnEqualSet;
19 _useReferenceEquals = useReferenceEquals;
20 _skipSetterOnNoChange = skipSetterOnNoChange;
21 }
22
23 private readonly bool _notifyOnEqualSet;
24 private readonly bool _useReferenceEquals;
25 private readonly bool _skipSetterOnNoChange;
26
27 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
28 {
29 if (context == null) throw new ArgumentNullException("context");
30
31 return context.IsSetter && (context.IsBeforeStep || context.IsAfterStep);
32 }
33
34 protected override void BeforeBuildAbstractSetter()
35 {
36 if (!_notifyOnEqualSet && Context.CurrentProperty.CanRead)
37 GenerateIsSameValueComparison();
38 }
39
40 protected override void BeforeBuildVirtualSetter()
41 {
42 if (!_notifyOnEqualSet && Context.CurrentProperty.CanRead)
43 GenerateIsSameValueComparison();
44 }
45
46 protected override void AfterBuildAbstractSetter()
47 {
48 BuildSetter();
49 }
50
51 protected override void AfterBuildVirtualSetter()
52 {
53 BuildSetter();
54 }
55
56 public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder)
57 {
58 if (typeBuilder is PropertyChangedBuilder)
59 return false;
60
61 return base.IsCompatible(context, typeBuilder);
62 }
63
64 public override int GetPriority(BuildContext context)
65 {
66 return TypeBuilderConsts.Priority.PropChange;
67 }
68
69 private LocalBuilder _isSameValueBuilder;
70 private Label _afterNotificationLabel;
71
72 private void GenerateIsSameValueComparison()
73 {
74 EmitHelper emit = Context.MethodBuilder.Emitter;
75
76 if (_skipSetterOnNoChange)
77 _afterNotificationLabel = emit.DefineLabel();
78 else
79 _isSameValueBuilder = emit.DeclareLocal(typeof(bool));
80
81 MethodInfo op_InequalityMethod =
82 Context.CurrentProperty.PropertyType.GetMethod("op_Inequality",
83 new Type[]
84 {
85 Context.CurrentProperty.PropertyType,
86 Context.CurrentProperty.PropertyType
87 });
88
89 if (op_InequalityMethod == null)
90 {
91 if (Context.CurrentProperty.PropertyType.IsValueType)
92 {
93 if (TypeHelper.IsNullableType(Context.CurrentProperty.PropertyType))
94 {
95 // Handled nullable types
96
97 var currentValue = emit.DeclareLocal(Context.CurrentProperty.PropertyType);
98 var newValue = emit.DeclareLocal(Context.CurrentProperty.PropertyType);
99 var notEqualLabel = emit.DefineLabel();
100 var comparedLabel = emit.DefineLabel();
101 var hasValueGetMethod = Context.CurrentProperty.PropertyType.GetProperty("HasValue").GetGetMethod();
102
103 emit
104 .ldarg_0
105 .callvirt(Context.CurrentProperty.GetGetMethod(true))
106 .stloc(currentValue)
107 .ldarg_1
108 .stloc(newValue)
109 .ldloca(currentValue)
110 .call(Context.CurrentProperty.PropertyType, "GetValueOrDefault")
111 .ldloca(newValue)
112 .call(Context.CurrentProperty.PropertyType, "GetValueOrDefault");
113
114 var nullableUnderlyingType = TypeHelper.GetUnderlyingType(Context.CurrentProperty.PropertyType);
115
116 op_InequalityMethod = nullableUnderlyingType.GetMethod("op_Inequality",
117 new Type[]
118 {
119 nullableUnderlyingType,
120 nullableUnderlyingType
121 });
122
123 if (op_InequalityMethod != null)
124 {
125 emit
126 .call(op_InequalityMethod)
127 .brtrue_s(notEqualLabel);
128 }
129 else
130 emit.bne_un_s(notEqualLabel);
131
132 emit
133 .ldloca(currentValue)
134 .call(hasValueGetMethod)
135 .ldloca(newValue)
136 .call(hasValueGetMethod)
137 .ceq
138 .ldc_bool(true)
139 .ceq
140 .br(comparedLabel)
141 .MarkLabel(notEqualLabel)
142 .ldc_bool(false)
143 .MarkLabel(comparedLabel)
144 .end();
145 }
146 else if (!Context.CurrentProperty.PropertyType.IsPrimitive)
147 {
148 // Handle structs without op_Inequality.
149 var currentValue = emit.DeclareLocal(Context.CurrentProperty.PropertyType);
150
151 emit
152 .ldarg_0
153 .callvirt(Context.CurrentProperty.GetGetMethod(true))
154 .stloc(currentValue)
155 .ldloca(currentValue)
156 .ldarg_1
157 .box(Context.CurrentProperty.PropertyType)
158 .constrained(Context.CurrentProperty.PropertyType)
159 .callvirt(typeof(object), "Equals", new [] {typeof(object)})
160 .end();
161 }
162 else
163 {
164 // Primitive value type comparison
165 emit
166 .ldarg_0
167 .callvirt(Context.CurrentProperty.GetGetMethod(true))
168 .ldarg_1
169 .ceq
170 .end();
171 }
172 }
173 else if (!_useReferenceEquals)
174 {
175 // Do not use ReferenceEquals comparison for objects
176 emit
177 .ldarg_0
178 .callvirt(Context.CurrentProperty.GetGetMethod(true))
179 .ldarg_1
180 .ceq
181 .end();
182 }
183 else
184 {
185 // ReferenceEquals comparison for objects
186 emit
187 .ldarg_0
188 .callvirt(Context.CurrentProperty.GetGetMethod(true))
189 .ldarg_1
190 .call(typeof(object), "ReferenceEquals", typeof(object), typeof(object))
191 .end();
192 }
193 }
194 else
195 {
196 // Items compared have op_Inequality operator (!=)
197 emit
198 .ldarg_0
199 .callvirt(Context.CurrentProperty.GetGetMethod(true))
200 .ldarg_1
201 .call(op_InequalityMethod)
202 .ldc_i4_0
203 .ceq
204 .end();
205 }
206
207 if (_skipSetterOnNoChange)
208 emit.brtrue(_afterNotificationLabel);
209 else
210 emit.stloc(_isSameValueBuilder);
211 }
212
213 private void BuildSetter()
214 {
215 InterfaceMapping im = Context.Type.GetInterfaceMap(typeof(IPropertyChanged));
216 MethodInfo mi = im.TargetMethods[0];
217 FieldBuilder ifb = GetPropertyInfoField();
218 EmitHelper emit = Context.MethodBuilder.Emitter;
219
220 if (!_notifyOnEqualSet && Context.CurrentProperty.CanRead && !_skipSetterOnNoChange)
221 {
222 _afterNotificationLabel = emit.DefineLabel();
223 emit
224 .ldloc (_isSameValueBuilder)
225 .brtrue(_afterNotificationLabel);
226 }
227
228 if (mi.IsPublic)
229 {
230 emit
231 .ldarg_0
232 .ldsfld (ifb)
233 .callvirt (mi)
234 ;
235 }
236 else
237 {
238 emit
239 .ldarg_0
240 .castclass (typeof(IPropertyChanged))
241 .ldsfld (ifb)
242 .callvirt (im.InterfaceMethods[0])
243 ;
244 }
245
246 if (!_notifyOnEqualSet && Context.CurrentProperty.CanRead)
247 emit.MarkLabel(_afterNotificationLabel);
248 }
249 }
250 }