{"id":21861325,"url":"https://github.com/seng3694/il-code-weaver-inotifypropertychanged-example","last_synced_at":"2025-06-12T11:37:46.032Z","repository":{"id":114139449,"uuid":"126358180","full_name":"Seng3694/IL-Code-Weaver-INotifyPropertyChanged-Example","owner":"Seng3694","description":"Mono.Cecil IL Code Weaver Example","archived":false,"fork":false,"pushed_at":"2018-04-11T23:16:37.000Z","size":20,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-07T13:43:19.398Z","etag":null,"topics":["codeweaving","csharp","inotifypropertychanged","monocecil","mvvm","wpf"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Seng3694.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-22T15:44:14.000Z","updated_at":"2023-08-21T13:17:10.000Z","dependencies_parsed_at":"2023-06-14T07:15:21.422Z","dependency_job_id":null,"html_url":"https://github.com/Seng3694/IL-Code-Weaver-INotifyPropertyChanged-Example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Seng3694/IL-Code-Weaver-INotifyPropertyChanged-Example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seng3694%2FIL-Code-Weaver-INotifyPropertyChanged-Example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seng3694%2FIL-Code-Weaver-INotifyPropertyChanged-Example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seng3694%2FIL-Code-Weaver-INotifyPropertyChanged-Example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seng3694%2FIL-Code-Weaver-INotifyPropertyChanged-Example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Seng3694","download_url":"https://codeload.github.com/Seng3694/IL-Code-Weaver-INotifyPropertyChanged-Example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Seng3694%2FIL-Code-Weaver-INotifyPropertyChanged-Example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259456405,"owners_count":22860548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codeweaving","csharp","inotifypropertychanged","monocecil","mvvm","wpf"],"created_at":"2024-11-28T03:11:11.825Z","updated_at":"2025-06-12T11:37:45.986Z","avatar_url":"https://github.com/Seng3694.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# C# Mono.Cecil IL Code Weaver INotifyPropertyChanged-Example\n\n## What's going on here?\n\nIn C# a basic class implementation may look like this:\n\n```csharp\npublic class ViewModel\n{\n    public int SomeValue { get; set; }\n}\n```\n\nWith WPF MVVM the basic implementation of a ViewModel will look something like this:\n```csharp\npublic class ViewModel : INotifyPropertyChanged\n{\n    public event PropertyChangedEventHandler PropertyChanged;\n\n    private int _someValue;\n    public int SomeValue\n    {\n        get { return _someValue; }\n        set\n        {\n            if(_someValue != value)\n            {\n                _someValue = value;\n                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(\"SomeValue\"));\n            }\n        }\n    }\n}\n```\n\nWell... this is some code to write for such a simple task. The most usual step would be to write an abstract class which minimizes this code (imagine the mess with more properties).\n\nAn abstract base ViewModel could look like this:\n```csharp\npublic abstract class BaseViewModel : INotifyPropertyChanged\n{\n    public event PropertyChangedEventHandler PropertyChanged;\n\n    protected virtual void SetField\u003cT\u003e(\n        ref T field, \n        T value, \n        IEqualityComparer\u003cT\u003e comparer = null, \n        [CallerMemberName]string propertyName = null)\n    {\n        if (comparer == null)\n            comparer = EqualityComparer\u003cT\u003e.Default;\n\n        if(!comparer.Equals(field, value))\n        {\n            field = value;\n            OnPropertyChanged(propertyName);\n        }\n    }\n\n    protected virtual void OnPropertyChanged(string propertyName = null)\n    {\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n    }\n}\n```\n\nThe same ViewModel which implements the base:\n\n```csharp\npublic class ViewModel : BaseViewModel\n{\n    private int _someValue;\n    public int SomeValue\n    {\n        get { return _someValue; }\n        set { SetField(ref _someValue, value); }\n    }\n}\n```\n\nWorks like a charm and is the easiest way to simplify a ViewModel.\n\n## Nothing new to me...\n\nWouldn't it be nice if someone could just write the property itself without backing field and setter logic? \n\n```csharp\npublic class ViewModel : INotifyPropertyChanged\n{\n    public event PropertyChangedEventHandler PropertyChanged;\n        \n    [NotifyPropertyChanged]\n    public int SomeValue { get; set; }\n}\n```\n\nThe concept of \u003cabbr title=\"Aspect Oriented Programming\"\u003eAOP\u003c/abbr\u003e would allow some kind of behavior. Sadly C# does not support AOP directly. And that is what IL Code Weaving is for. With frameworks like Mono.Cecil someone can write IL instructions directly into an assembly.\n\n### What have to be changed so the basic ViewModel implementation looks like the AOP example but with the same behavior?\n- A auto property with `{ get; set; }` does actually have a invisible backing field named `\u003cPropertyName\u003ek__BackingField`. So we don't have to actually create that\n- The `get` is exactly the same with or without the INotifyPropertyChanged implementation\n- So there is just the `set` method left\n\nLooking at the IL Code of the property setter in the basic ViewModel implementation we can see the following (decompiler used in this example [JetBrains dotPeek][1]):\n\n```csharp\n.method public hidebysig specialname instance void \n    set_SomeValue(\n      int32 'value'\n    ) cil managed \n  {\n    .maxstack 3\n    .locals init (\n      [0] bool V_0\n    )\n\n    // [27 13 - 27 14]\n    IL_0000: nop          \n\n    // [28 17 - 28 41]\n    IL_0001: ldarg.0      // this\n    IL_0002: ldfld        int32 TestApp.ViewModels3.ViewModel::_someValue\n    IL_0007: ldarg.1      // 'value'\n    IL_0008: ceq          \n    IL_000a: ldc.i4.0     \n    IL_000b: ceq          \n    IL_000d: stloc.0      // V_0\n\n    IL_000e: ldloc.0      // V_0\n    IL_000f: brfalse.s    IL_0037\n\n    // [29 17 - 29 18]\n    IL_0011: nop          \n\n    // [30 21 - 30 40]\n    IL_0012: ldarg.0      // this\n    IL_0013: ldarg.1      // 'value'\n    IL_0014: stfld        int32 TestApp.ViewModels3.ViewModel::_someValue\n\n    // [31 21 - 31 94]\n    IL_0019: ldarg.0      // this\n    IL_001a: ldfld        class [System]System.ComponentModel.PropertyChangedEventHandler TestApp.ViewModels3.ViewModel::PropertyChanged\n    IL_001f: dup          \n    IL_0020: brtrue.s     IL_0025\n    IL_0022: pop          \n    IL_0023: br.s         IL_0036\n    IL_0025: ldarg.0      // this\n    IL_0026: ldstr        \"SomeValue\"\n    IL_002b: newobj       instance void [System]System.ComponentModel.PropertyChangedEventArgs::.ctor(string)\n    IL_0030: callvirt     instance void [System]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class [System]System.ComponentModel.PropertyChangedEventArgs)\n    IL_0035: nop          \n\n    // [32 17 - 32 18]\n    IL_0036: nop          \n\n    // [33 13 - 33 14]\n    IL_0037: ret          \n\n  } // end of method ViewModel::set_SomeValue\n```\n\nThe standard setter looks like this:\n\n```csharp\n.method public hidebysig specialname instance void \n    set_SomeValue(\n      int32 'value'\n    ) cil managed \n  {\n    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() \n      = (01 00 00 00 )\n    .maxstack 8\n\n    // [14 37 - 14 41]\n    IL_0000: ldarg.0      // this\n    IL_0001: ldarg.1      // 'value'\n    IL_0002: stfld        int32 TestApp.ViewModels.ViewModel2::'\u003cSomeValue\u003ek__BackingField'\n    IL_0007: ret          \n\n  } // end of method ViewModel2::set_SomeValue\n```\n\nSo basically someone have to copy and paste the instructions right in there. This project does exactly this task.\n\n## Get the project (Visual Studio)\n1. Clone the repository\n    ```\n    git clone \"https://github.com/Seng3694/IL-Code-Weaver-INotifyPropertyChanged-Example\" ILCodeWeaver\n    ```\n\n2. Open the `ILCodeWeaving.sln` Solution file\n3. Make sure TestApp is the StartUp project\n4. Notice the post-build task in the TestApp project. This will execute the Weaver.exe with the TestApp.exe path as an argument\n    ```\n    \"$(SolutionDir)Weaver\\bin\\$(ConfigurationName)\\Weaver.exe\" \"$(TargetPath)\"\n    ```\n5. Notice the project reference from TestApp to Weaver. This ensures that the Weaver.exe is built before the TestApp will be built\n6. Running the TestApp and typing something in the left TextBox should look like this:\n![Test Run][2]\n\n[1]:https://www.jetbrains.com/decompiler/\n[2]:https://cdn.discordapp.com/attachments/425728769236664350/426434401405108224/unknown.png","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseng3694%2Fil-code-weaver-inotifypropertychanged-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseng3694%2Fil-code-weaver-inotifypropertychanged-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseng3694%2Fil-code-weaver-inotifypropertychanged-example/lists"}