Extends Verify to allow verification of assemblies via ICSharpCode.Decompiler.
See Milestones for release notes.
Entity Framework Extensions is a major sponsor and is proud to contribute to the development this project.
[ModuleInitializer]publicstaticvoidInit()=>VerifyICSharpCodeDecompiler.Initialize();Then given the following type:
publicclassTarget:INotifyPropertyChanged{voidOnPropertyChanged([CallerMemberName]string?propertyName=null)=>PropertyChanged?.Invoke(this,new(propertyName));publiceventPropertyChangedEventHandler?PropertyChanged;string?property;publicstring?Property{get=>property;set{property=value;OnPropertyChanged();}}}[Test]publicasyncTaskTypeDefinitionUsage(){usingvarfile=newPEFile(assemblyPath);vartype=file.Metadata.TypeDefinitions.Single(
_ =>{varfullName=_.GetFullTypeName(file.Metadata);returnfullName.Name=="Target";});awaitVerify(newTypeToDisassemble(file,type));}Result:
.classpublicautoansibeforefieldinit Target
extends[System.Runtime]System.Object
implements[System.ObjectModel]System.ComponentModel.INotifyPropertyChanged
{
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
0100000000
)
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
0100020000
)
.interfaceimpl type [System.ObjectModel]System.ComponentModel.INotifyPropertyChanged
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
0100000000
)
// Fields.fieldprivatestring'property'.fieldprivateclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler PropertyChanged
.custominstancevoid[System.Runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype[System.Runtime]System.Diagnostics.DebuggerBrowsableState) = (
0100000000000000
)
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01000000
)
// Methods.methodpublichidebysigspecialnamertspecialnameinstancevoid.ctor () cilmanaged {
// Header size: 1// Code size: 8 (0x8).maxstack8IL_0000: ldarg.0IL_0001: callinstancevoid[System.Runtime]System.Object::.ctor()
IL_0006: nopIL_0007: ret
} // end of method Target::.ctor.methodpublicfinalhidebysigspecialnamenewslotvirtualinstancevoid add_PropertyChanged (
class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler 'value'
) cilmanaged {
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01000000
)
// Header size: 12// Code size: 41 (0x29).maxstack3.localsinit (
[0] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
[1] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
[2] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
)
IL_0000: ldarg.0IL_0001: ldfldclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_0006: stloc.0// loop start (head: IL_0007)IL_0007: ldloc.0IL_0008: stloc.1IL_0009: ldloc.1IL_000a: ldarg.1IL_000b: callclass[System.Runtime]System.Delegate [System.Runtime]System.Delegate::Combine(class[System.Runtime]System.Delegate, class[System.Runtime]System.Delegate)
IL_0010: castclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
IL_0015: stloc.2IL_0016: ldarg.0IL_0017: ldfldaclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_001c: ldloc.2IL_001d: ldloc.1IL_001e: call !!0[System.Threading]System.Threading.Interlocked::CompareExchange<class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler>(!!0&, !!0, !!0)
IL_0023: stloc.0IL_0024: ldloc.0IL_0025: ldloc.1IL_0026: bne.un.s IL_0007
// end loopIL_0028: ret
} // end of method Target::add_PropertyChanged.methodpublichidebysigspecialnameinstancestring get_Property () cilmanaged {
// Header size: 1// Code size: 7 (0x7).maxstack8IL_0000: ldarg.0IL_0001: ldfldstring Target::'property'IL_0006: ret
} // end of method Target::get_Property.methodprivatehidebysiginstancevoid OnPropertyChanged (
[opt]string propertyName
) cilmanaged {
.param [1] = nullref.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.CallerMemberNameAttribute::.ctor() = (
01000000
)
// Header size: 1// Code size: 26 (0x1a).maxstack8IL_0000: ldarg.0IL_0001: ldfldclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_0006: dupIL_0007: brtrue.s IL_000c
IL_0009: popIL_000a: br.s IL_0019
IL_000c: ldarg.0IL_000d: ldarg.1IL_000e: newobjinstancevoid[System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs::.ctor(string)
IL_0013: callvirtinstancevoid[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class[System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
IL_0018: nopIL_0019: ret
} // end of method Target::OnPropertyChanged.methodpublicfinalhidebysigspecialnamenewslotvirtualinstancevoid remove_PropertyChanged (
class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler 'value'
) cilmanaged {
.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01000000
)
// Header size: 12// Code size: 41 (0x29).maxstack3.localsinit (
[0] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
[1] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
[2] class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
)
IL_0000: ldarg.0IL_0001: ldfldclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_0006: stloc.0// loop start (head: IL_0007)IL_0007: ldloc.0IL_0008: stloc.1IL_0009: ldloc.1IL_000a: ldarg.1IL_000b: callclass[System.Runtime]System.Delegate [System.Runtime]System.Delegate::Remove(class[System.Runtime]System.Delegate, class[System.Runtime]System.Delegate)
IL_0010: castclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
IL_0015: stloc.2IL_0016: ldarg.0IL_0017: ldfldaclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_001c: ldloc.2IL_001d: ldloc.1IL_001e: call !!0[System.Threading]System.Threading.Interlocked::CompareExchange<class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler>(!!0&, !!0, !!0)
IL_0023: stloc.0IL_0024: ldloc.0IL_0025: ldloc.1IL_0026: bne.un.s IL_0007
// end loopIL_0028: ret
} // end of method Target::remove_PropertyChanged.methodpublichidebysigspecialnameinstancevoid set_Property (
string'value'
) cilmanaged {
// Header size: 1// Code size: 21 (0x15).maxstack8IL_0000: nopIL_0001: ldarg.0IL_0002: ldarg.1IL_0003: stfldstring Target::'property'IL_0008: ldarg.0IL_0009: ldstr"Property"IL_000e: callinstancevoid Target::OnPropertyChanged(string)
IL_0013: nopIL_0014: ret
} // end of method Target::set_Property// Events.event[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler PropertyChanged
{
.addoninstancevoid Target::add_PropertyChanged(class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler)
.removeoninstancevoid Target::remove_PropertyChanged(class[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler)
}
// Properties.propertyinstancestring Property()
{
.getinstancestring Target::get_Property()
.setinstancevoid Target::set_Property(string)
}
} // end of class TargetA string for the type name can also be used:
[Test]publicasyncTaskTypeNameUsage(){usingvarfile=newPEFile(assemblyPath);awaitVerify(newTypeToDisassemble(file,"Target"));}[Test]publicasyncTaskMethodNameUsage(){usingvarfile=newPEFile(assemblyPath);awaitVerify(newMethodToDisassemble(file,"Target","OnPropertyChanged"));}Result:
.methodprivatehidebysiginstancevoid OnPropertyChanged (
[opt]string propertyName
) cilmanaged {
.param [1] = nullref.custominstancevoid[System.Runtime]System.Runtime.CompilerServices.CallerMemberNameAttribute::.ctor() = (
01000000
)
// Header size: 1// Code size: 26 (0x1a).maxstack8IL_0000: ldarg.0IL_0001: ldfldclass[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
IL_0006: dupIL_0007: brtrue.s IL_000c
IL_0009: popIL_000a: br.s IL_0019
IL_000c: ldarg.0IL_000d: ldarg.1IL_000e: newobjinstancevoid[System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs::.ctor(string)
IL_0013: callvirtinstancevoid[System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class[System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
IL_0018: nopIL_0019: ret
} // end of method Target::OnPropertyChanged[Test]publicasyncTaskPropertyPartsUsage(){usingvarfile=newPEFile(assemblyPath);awaitVerify(newPropertyToDisassemble(file,"Target","Property",PropertyParts.GetterAndSetter));}Result:
.methodpublichidebysigspecialnameinstancestring get_Property () cilmanaged {
// Header size: 1// Code size: 7 (0x7).maxstack8IL_0000: ldarg.0IL_0001: ldfldstring Target::'property'IL_0006: ret
} // end of method Target::get_Property.methodpublichidebysigspecialnameinstancevoid set_Property (
string'value'
) cilmanaged {
// Header size: 1// Code size: 21 (0x15).maxstack8IL_0000: nopIL_0001: ldarg.0IL_0002: ldarg.1IL_0003: stfldstring Target::'property'IL_0008: ldarg.0IL_0009: ldstr"Property"IL_000e: callinstancevoid Target::OnPropertyChanged(string)
IL_0013: nopIL_0014: ret
} // end of method Target::set_PropertyStarting with version 3.2 the generated IL is normalized by default, to avoid failed tests only because the binary layout has changed:
- types and members are sorted by name
- RVA adress comments are stripped
To turn of the sorting, use the DontNormalizeIL setting. It will then decompile IL reflecting the original layout:
[Test]publicasyncTaskBackwardCompatibility(){usingvarfile=newPEFile(assemblyPath);awaitVerify(newTypeToDisassemble(file,"Target")).DontNormalizeIl();}Gem designed by Bakunetsu Kaito from The Noun Project.

