-
-
Notifications
You must be signed in to change notification settings - Fork 234
Description
Describe the issue
PropertyChanging.Fody interferes with PropertyChanged.Fody option "CheckForEquality".
Assuming that the two modules are intended to be compatible:
#169
Basic behavior seems to be correct. But PropertyChanged.Fody's CheckForEquality = false will be ignored for classes also implementing INotifyPropertyChanging, I assume due to weavers conflicting in some way.
For a class like:
[AddINotifyPropertyChangedInterface]
public class TestClassImplicitNoCheckEquality
{
[DoNotCheckEquality]
public object Value { get; set; }
}
The relevant part of IL code with PropertyChanged.Fody alone will look like:
.method public hidebysig specialname instance void
set_Value(
object 'value'
) cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 2
// [12 36 - 12 40]
IL_0000: ldarg.0 // this
IL_0001: ldarg.1 // 'value'
IL_0002: stfld object FodyTest.TestClassImplicit::'<Value>k__BackingField'
IL_0007: ldarg.0 // this
IL_0008: ldsfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs 'FodyCDoubleNet.<>PropertyChangedEventArgs'::Value
IL_000d: callvirt instance void FodyTest.TestClassImplicit::'<>OnPropertyChanged'(class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
IL_0012: nop
IL_0013: ret
} // end of method TestClassImplicit::set_Value
With added INotifyPropertyChanging and PropertyChanging.Fody:
.method public hidebysig specialname instance void
set_Value(
object 'value'
) cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 2
IL_0000: ldarg.0 // this
IL_0001: ldfld object FodyTest.TestClassImplicit::'<Value>k__BackingField'
IL_0006: ldarg.1 // 'value'
IL_0007: call bool [System.Runtime]System.Object::Equals(object, object)
IL_000c: brfalse.s IL_000f
IL_000e: ret
IL_000f: nop
// [18 36 - 18 40]
IL_0010: ldarg.0 // this
IL_0011: ldarg.1 // 'value'
IL_0012: stfld object FodyTest.TestClassImplicit::'<Value>k__BackingField'
IL_0017: ldarg.0 // this
IL_0018: ldsfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs 'FodyCDoubleNet.<>PropertyChangedEventArgs'::Value
IL_001d: callvirt instance void FodyTest.TestClassImplicit::'<>OnPropertyChanged'(class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
IL_0022: nop
IL_0023: ret
} // end of method TestClassImplicit::set_Value
Diff of IL codes:
The first class will fire an event on assignment of same value; the second class will ignore it despite the DoNotCheckEquality attribute.
The same happens if the option is configured via weavers.xml
The same happens if the interface is explicitly implemented without the AddINotifyPropertyChangedInterface attribute.
Minimal Repro
Used the following test on a set of test classes:
public class UnitTestReflection
{
[Theory]
[InlineData(typeof(TestClassExplicit), 1, 1, 1)]
[InlineData(typeof(TestClassExplicit), 1, 2, 2)]
[InlineData(typeof(TestClassImplicit), 1, 1, 1)]
[InlineData(typeof(TestClassImplicit), 1, 2, 2)]
[InlineData(typeof(TestClassImplicitNoCheckEquality), 1, 1, 2)]
[InlineData(typeof(TestClassImplicitNoCheckEquality), 1, 2, 2)]
public void CorrectFireAll(Type type, object firstValue, object secondValue, int expectedCount)
{
var a = Activator.CreateInstance(type);
var props = type.GetProperties();
var valueProp = props.Single(x => x.PropertyType == typeof(object) && x.Name == "Value");
var ap = (INotifyPropertyChanged)a;
var count = 0;
ap.PropertyChanged += (p, e) => count++;
valueProp.SetValue(a, firstValue);
valueProp.SetValue(a, secondValue);
Assert.Equal(expectedCount, count);
}
}
The class & test projects (VS2022 Community 17.12, NET9.0, Fody 6.9.1, PropertyChanged.Fody 4.1.0, PropertyChanging.Fody 1.30.3) :
I can add a PR with the tests if desired, just wasn't sure which repo it should go to - here makes more sense I think.
