Mercurial > pub > bltoolkit
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 } |