@@ -24,18 +24,28 @@ public abstract class ObservableValidator : ObservableObject, INotifyDataErrorIn
2424 /// </summary>
2525 private readonly Dictionary < string , List < ValidationResult > > errors = new Dictionary < string , List < ValidationResult > > ( ) ;
2626
27- /// <summary>
28- /// Indicates the total number of properties with errors (not total errors).
29- /// This is used to allow <see cref="HasErrors"/> to operate in O(1) time, as it can just
30- /// check whether this value is not 0 instead of having to traverse <see cref="errors"/>.
31- /// </summary>
32- private int totalErrors ;
33-
3427 /// <inheritdoc/>
3528 public event EventHandler < DataErrorsChangedEventArgs > ? ErrorsChanged ;
3629
3730 /// <inheritdoc/>
38- public bool HasErrors => this . totalErrors > 0 ;
31+ public bool HasErrors
32+ {
33+ get
34+ {
35+ // This uses the value enumerator for Dictionary<TKey, TValue>.ValueCollection, so it doesn't
36+ // allocate. Accessing this property is O(n), but we can stop as soon as we find at least one
37+ // error in the whole entity, and doing this saves 8 bytes in the object size (no fields needed).
38+ foreach ( var value in this . errors . Values )
39+ {
40+ if ( value . Count > 0 )
41+ {
42+ return true ;
43+ }
44+ }
45+
46+ return false ;
47+ }
48+ }
3949
4050 /// <summary>
4151 /// Compares the current and new values for a given property. If the value has changed,
@@ -273,28 +283,10 @@ private void ValidateProperty(object? value, string? propertyName)
273283 new ValidationContext ( this , null , null ) { MemberName = propertyName } ,
274284 propertyErrors ) ;
275285
276- // Update the state and/or the errors for the property
277- if ( isValid )
278- {
279- if ( errorsChanged )
280- {
281- this . totalErrors -- ;
282- }
283- }
284- else
285- {
286- if ( ! errorsChanged )
287- {
288- this . totalErrors ++ ;
289- }
290-
291- errorsChanged = true ;
292- }
293-
294286 // Only raise the event once if needed. This happens either when the target property
295287 // had existing errors and is now valid, or if the validation has failed and there are
296288 // new errors to broadcast, regardless of the previous validation state for the property.
297- if ( errorsChanged )
289+ if ( errorsChanged || ! isValid )
298290 {
299291 ErrorsChanged ? . Invoke ( this , new DataErrorsChangedEventArgs ( propertyName ) ) ;
300292 }
0 commit comments