{"id":22069872,"url":"https://github.com/romagny13/nintercept","last_synced_at":"2025-03-23T18:43:44.746Z","repository":{"id":38143009,"uuid":"273794242","full_name":"romagny13/NIntercept","owner":"romagny13","description":"Interception for NET","archived":false,"fork":false,"pushed_at":"2022-12-08T10:47:18.000Z","size":18756,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-31T22:33:41.106Z","etag":null,"topics":["interception","msil","nintercept","proxy"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/romagny13.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["romagny13"]}},"created_at":"2020-06-20T22:31:56.000Z","updated_at":"2024-04-19T02:47:20.000Z","dependencies_parsed_at":"2023-01-25T01:30:15.877Z","dependency_job_id":null,"html_url":"https://github.com/romagny13/NIntercept","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romagny13%2FNIntercept","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romagny13%2FNIntercept/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romagny13%2FNIntercept/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romagny13%2FNIntercept/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romagny13","download_url":"https://codeload.github.com/romagny13/NIntercept/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236112927,"owners_count":19096850,"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":["interception","msil","nintercept","proxy"],"created_at":"2024-11-30T20:13:38.622Z","updated_at":"2025-01-29T00:52:01.357Z","avatar_url":"https://github.com/romagny13.png","language":"C#","funding_links":["https://github.com/sponsors/romagny13"],"categories":[],"sub_categories":[],"readme":"# NIntercept\n\n\u003e Allows to create **proxies** for classes and interfaces, **intercept** properties, methods, events and **customize the code generated**.\n\n\nProxies\n\n* **Class Proxy**: a **Proxy** that inherits from the **class** is created. **Virtual** members (properties, methods end events) are **overridden**. The **base class members** are **invoked** after interception.\n* **Class Proxy with target**: The **target members** are **invoked** after interception.\n* **Interface Proxy with target**: a **Proxy** that implements the **interface** is created. The **target members** are **invoked** after interception.\n* **Interface Proxy without target**: interceptors are used to get and set the **return value**\n\nInterception:\n\n* **by attribute** (built-in or custom)\n* **global interceptor**\n* Implement **IInterceptor** or use **Interceptor** and **AsyncInterceptor** base classes\n\n\nInvocation Members:\n\n* **CallerMethod**: the method of the proxy\n* **InterceptorProviderType**: IGetterInterceptorProvider, ISetterInterceptorProvider,IMethodInterceptorProvider ,IAddOnInterceptorProvider, IRemoveOnInterceptorProvider\n* **Member** : property, method, event of the class to invoke after interception\n* **Parameters**: the method parameters\n* **Proxy**: the class or interface proxy\n* **ReturnValue**: the result value\n* **Target**: the target or null\n* **GetAwaitableContext**: allows to wait before calling Proceed\n* **GetParameter** allows to get typed parameter value\n* **Proceed**: call next interceptor or invoke member\n\nOptions:\n\n* **Mixins**: allows to **add features** to **proxy** created.\n* **AdditionalCode** : allows to **customize the code generated**.\n* **ClassProxyMemberSelector**: allows to **filter members to include** for **Class Proxy**. For example create an attribute and include only virtual members decorated with the attribute.\n* **AdditionalTypeAttributes**: allows to add **custom attributes** on proxy generated.\n* **ConstructorSelector**: allows to **select** the **base constructor** to call.\n* **InterceptableMethodBuilder**: allows to provide a custom **InterceptableMethodBuilder**.\n* **InterceptorSelector**: usefull to **select interceptors** to use for each method **when we don't use attributes**.\n\nAnd\n\n* **ConstructorInjectionResolver**: allows to **resolve constructor injections**.\n\nSupported:\n\n* _Generics_\n* _Parameters ref and out_\n* _Multi signatures_\n* Interception on _private members_ for **ClassProxy**\n\n## Install\n\n[NuGet Package](https://www.nuget.org/packages/NIntercept/)\n\n![Nuget](https://img.shields.io/nuget/v/nintercept.svg?style=for-the-badge)\n\n```\nInstall-Package NIntercept\n```\n\n## Samples\n\n### Class Proxy\n\n\u003e A **Proxy** that inherits from the **class** is created. **Virtual** members (properties, methods end events) are **overridden**. **Interceptors** allow to intercept these members. The **base class members** are **invoked** after interception.\n\n**Sample 1** with global interceptor\n\nCreate a **class** with **virtual** members\n\n```cs\npublic class MyClass\n{\n    public virtual string MyProperty { get; set; }\n\n    public virtual void MyMethod()\n    {\n\n    }\n\n    public virtual event EventHandler MyEvent;\n}\n```\n\nCreate an **interceptor**\n\n```cs\npublic class LogInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        Console.WriteLine($\"[LogInterceptor] Before {invocation.CallerMethod.Name}\");\n\n        invocation.Proceed();\n\n        Console.WriteLine($\"[LogInterceptor] After {invocation.CallerMethod.Name}\");\n    }\n}\n```\n\nCreate and use the **proxy**\n\n```cs\nvar generator = new ProxyGenerator();\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e(new LogInterceptor());\n\nproxy.MyProperty = \"New Value\"; // Setter\n\nConsole.WriteLine($\"MyProperty: {proxy.MyProperty}\"); // Getter\n\nproxy.MyMethod(); // Method\n\nEventHandler handler = null;\nhandler = (s, e) =\u003e\n{\n\n};\nproxy.MyEvent += handler; // AddOn\n\nproxy.MyEvent -= handler; // RemoveOn\n```\n\n**Output**\n\n```\n[LogInterceptor] Before set_MyProperty\n[LogInterceptor] After set_MyProperty\n[LogInterceptor] Before get_MyProperty\n[LogInterceptor] After get_MyProperty\nMyProperty: New Value\n[LogInterceptor] Before MyMethod\n[LogInterceptor] After MyMethod\n[LogInterceptor] Before add_MyEvent\n[LogInterceptor] After add_MyEvent\n[LogInterceptor] Before remove_MyEvent\n[LogInterceptor] After remove_MyEvent\n```\n\n**Sample 2** : **with attributes** (Create some interceptors for each attribute)\n\n```cs\n[AllInterceptor(typeof(LogInterceptor))]\npublic class MyClass\n{\n    [GetterInterceptor(typeof(MyGetterInterceptor))]\n    [SetterInterceptor(typeof(MySetterInterceptor))]\n    public virtual string MyProperty { get; set; }\n\n    [MethodInterceptor(typeof(MyMethodInterceptor))]\n    public virtual void MyMethod()\n    {\n\n    }\n\n    [AddOnInterceptor(typeof(MyAddOnInterceptor))]\n    [RemoveOnInterceptor(typeof(MyRemoveOnInterceptor))]\n    public virtual event EventHandler MyEvent;\n}\n```\n\n```cs\nvar generator = new ProxyGenerator();\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e();\n\n// etc.\n```\n\n| Interface | Target | Attribute |\n| --- | --- | --- |\n| IInterceptorProvider | class / interface | AllInterceptorAttribute\n| IGetterInterceptorProvider | Property getter | GetterInterceptorAttribute\n| ISetterInterceptorProvider | Property setter | SetterInterceptorAttribute\n| IMethodInterceptorProvider | Method | MethodInterceptorAttribute\n| IAddOnInterceptorProvider | Add event | AddOnInterceptorAttribute\n| IRemoveOnInterceptorProvider | Remove event | RemoveOnInterceptorAttribute\n\n**Sample 3**: Create a **custom interceptor attribute**\n\n\n```cs\n[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property)]\npublic class MyGetSetInterceptorAttribute : Attribute,\n    IGetterInterceptorProvider,\n    ISetterInterceptorProvider\n{\n    public Type InterceptorType =\u003e typeof(MyGetSetInterceptor);\n}\n\npublic class MyGetSetInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        Console.WriteLine($\"[MyGetSetInterceptor] Before {invocation.CallerMethod.Name}\");\n\n        invocation.Proceed();\n\n        Console.WriteLine($\"[MyGetSetInterceptor] After {invocation.CallerMethod.Name}\");\n    }\n}\n```\n\n**Decorate** the property\n\n```cs\npublic class MyClass\n{\n    [MyGetSetInterceptor]\n    public virtual string MyProperty { get; set; }\n}\n```\n\n**Output**\n\n```\n[MyGetSetInterceptor] Before set_MyProperty\n[MyGetSetInterceptor] After set_MyProperty\n[MyGetSetInterceptor] Before get_MyProperty\n[MyGetSetInterceptor] After get_MyProperty\nMyProperty: New Value\n```\n\n_Notes:_\n\n* The interceptor allows to update parameters, the return value\n\n**Sample**\n\n```cs\npublic class MyInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        invocation.Parameters[0] = \"New value\";\n\n        invocation.Proceed();\n\n        invocation.ReturnValue = \"New result\";\n    }\n}\n```\n\n* We can use the `Interceptor` class (`Proceed` is automatically invoked)\n\n```cs\npublic class MyInterceptor : Interceptor\n{\n    protected override void OnEnter(IInvocation invocation)\n    {\n        \n    }\n\n    protected override void OnException(IInvocation invocation, Exception exception)\n    {\n        \n    }\n\n    protected override void OnExit(IInvocation invocation)\n    {\n        \n    }\n}\n```\n\n### Class Proxy with target\n\n\u003e A **Proxy** that inherits from the **class** is created. **Virtual** members (properties, methods end events) are **overridden**. **Interceptors** allow to intercept these members. The **target members** are **invoked** after interception.\n\n```cs\nvar target = new MyClass();\nvar proxy = generator.CreateClassProxyWithTarget\u003cMyClass\u003e(target, new MyInterceptor());\nproxy.Method();\n```\n\n### Interface Proxy with target\n\n\u003e A **Proxy** that implements the **interface** is created. **Interceptors** allow to intercept these members. The **target members** are **invoked** after interception.\n\n\n```cs\nvar target = new MyService();\nvar proxy = generator.CreateInterfaceProxyWithTarget\u003cIMyService\u003e(target, new MyInterceptor());\nproxy.Method();\n```\n\n_Note: for Interface Proxy, add interceptor attributes on interface members_\n\n### Interface Proxy without target\n\n\u003e A **Proxy** that implements the **interface** is created. The **interceptors** are used to get and set the **return value**.\n\n```cs\npublic interface IMyService\n{\n    [MethodInterceptor(typeof(GetMessageInterceptor))]\n    string GetMessage(string name);\n}\n\npublic class GetMessageInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        // invocation.Proceed(); DO NOT CALL Proceed\n\n        invocation.ReturnValue = $\"Hello {invocation.Parameters[0]}!\";\n    }\n}\n```\n\n```cs\nvar proxy = generator.CreateInterfaceProxyWithoutTarget\u003cIMyService\u003e();\nvar message = proxy.GetMessage(\"World\");\nConsole.WriteLine(message); // write \"Hello World!\"\n```\n\n## Async interception \n\n**Sample 1** with `GetAwaitableContext`\n\n```cs\npublic class MyAsyncInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        var context = invocation.GetAwaitableContext(); // here\n\n        var viewModel = invocation.Proxy as MainWindowViewModel;\n        if (viewModel != null)\n            viewModel.IsBusy = true;\n\n        // async await void best practices\n        Task.Delay(3000).Await(() =\u003e\n        {\n            if (viewModel != null)\n                viewModel.IsBusy = false;\n\n            context.Proceed(); // here\n\n        }, ex =\u003e { });\n    }\n}\n```\n\n**Sample 2** With `AsyncInterceptor` for async Method\n\n```cs\npublic class MyClass\n{\n    public virtual async Task Method()\n    {\n        Console.WriteLine(\"Enter async Method\");\n        await Task.Delay(2000);\n        Console.WriteLine(\"Exit async Method\");\n    }\n}\n\npublic class MyAsyncInterceptor : AsyncInterceptor\n{\n    protected override void OnEnter(IInvocation invocation)\n    {\n        Console.WriteLine($\"[MyAsyncInterceptor] Enter '{invocation.Member.Name}'\");\n    }\n\n    protected override void OnException(IInvocation invocation, Exception exception)\n    {\n        Console.WriteLine($\"[MyAsyncInterceptor] Exception '{invocation.Member.Name}', {exception.Message}\");\n    }\n\n    protected override void OnExit(IInvocation invocation)\n    {\n        Console.WriteLine($\"[MyAsyncInterceptor] Exit '{invocation.Member.Name}'\");\n    }\n}\n```\n\n## ClassProxy Member Selector\n\n\u003e Allows to **filter members to include** for **Class Proxy**.\n\n**Sample:** ignore virtual Method named \"MyMethod\"\n\n```cs\npublic class MyClassProxyMemberSelector : ClassProxyMemberSelector\n{\n    public override bool Filter(MemberInfo member)\n    {\n        if (member.DeclaringType == typeof(MyClass) \u0026\u0026 member.Name == \"MyMethod\")\n            return false;\n\n        return true;\n    }\n}\npublic class MyClass\n{\n    public virtual void MyMethod()\n    {\n        Console.WriteLine(\"In MyMethod\");\n    }\n}\n```\n\nDefine the option\n\n```cs\nvar generator = new ProxyGenerator();\nvar options = new ProxyGeneratorOptions();\noptions.ClassProxyMemberSelector = new MyClassProxyMemberSelector();\n\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e(options, new LogInterceptor());\n\nproxy.MyMethod(); // not intercepted\n```\n\n## InterceptorSelector\n\n\u003e Usefull to select interceptors to use for each method when we don't use attributes.\n\n**Sample**\n\n```cs\npublic class MyInterceptorSelector : IInterceptorSelector\n{\n    private static readonly IInterceptor[] emptyInterceptorArray = new IInterceptor[0];\n\n    public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)\n    {\n        if (IsGetMethod(method))\n        {\n            var getMethodInterceptor = interceptors.First(p =\u003e p.GetType() == typeof(GetMethodInterceptor));\n            return new IInterceptor[] { getMethodInterceptor };\n        }\n        else if (IsSetMethod(method))\n        {\n            var setMethodInterceptor = interceptors.First(p =\u003e p.GetType() == typeof(SetMethodInterceptor));\n            return new IInterceptor[] { setMethodInterceptor };\n        }\n        else if (IsAddmethod(method)) { }\n        else if (IsRemoveMethod(method)) { }\n        else\n        {\n            var methodInterceptor = interceptors.First(p =\u003e p.GetType() == typeof(MethodInterceptor));\n            return new IInterceptor[] { methodInterceptor };\n        }\n        return emptyInterceptorArray;\n    }\n\n    private bool IsGetMethod(MethodInfo method)\n    {\n        return method.Name.StartsWith(\"get_\");\n    }\n\n    private bool IsSetMethod(MethodInfo method)\n    {\n        return method.Name.StartsWith(\"set_\");\n    }\n\n    private bool IsAddmethod(MethodInfo method)\n    {\n        return method.Name.StartsWith(\"add_\");\n    }\n\n    private bool IsRemoveMethod(MethodInfo method)\n    {\n        return method.Name.StartsWith(\"remove_\");\n    }\n}\n\npublic class GetMethodInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        Console.WriteLine($\"[GetMethodInterceptor] {invocation.CallerMethod.Name}\");\n        invocation.Proceed();\n    }\n}\n\npublic class SetMethodInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        Console.WriteLine($\"[SetMethodInterceptor] {invocation.CallerMethod.Name}\");\n        invocation.Proceed();\n    }\n}\n\npublic class MethodInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        Console.WriteLine($\"[MethodInterceptor] {invocation.CallerMethod.Name}\");\n        invocation.Proceed();\n    }\n}\n\npublic class MyClass\n{\n    public virtual string MyProperty { get; set; }\n\n    public virtual void MyMethod()\n    {\n        Console.WriteLine(\"In MyMethod\");\n    }\n}\n```\n\nDefine the options\n\n```cs\nvar generator = new ProxyGenerator();\nvar options = new ProxyGeneratorOptions();\noptions.InterceptorSelector = new MyInterceptorSelector();\nvar proxy = generator.CreateClassProxy\u003cMyClassWithInterceptorSelector\u003e(options, new GetMethodInterceptor(), new SetMethodInterceptor(), new MethodInterceptor());\n\nproxy.MyProperty = \"New Value\"; // SetMethodInterceptor used\n\nConsole.WriteLine($\"Get: {proxy.MyProperty}\"); // GetMethodInterceptor used\n\nproxy.MyMethod(); // MethodInterceptor used\n```\n\n\n## Constructor Injection Resolver\n\n\u003e Allows to resolve **parameters** of the **base class contructor** for a **Class Proxy**.\n\n_Note: the DefaultConstructorInjectionResolver returns defaults (default values for value types, null for objects)._\n\n**Sample** use **Unity Container** to resolve injection parameters\n\n```cs\npublic class MyClass\n{\n    private readonly IMyService myService;\n  \n    // constructor with injection\n    public MyClass(IMyService myService)\n    {\n        this.myService = myService;\n    }\n\n    public void Method()\n    {\n        Console.WriteLine($\"{myService.GetMessage(\"World\")}\");\n    }\n}\n\npublic interface IMyService\n{\n    string GetMessage(string name);\n}\n\npublic class MyService : IMyService\n{\n    public string GetMessage(string name)\n    {\n        return $\"Hello {name}!\";\n    }\n}\n```\n\nCreate the `Constructor Injection Resolver`\n\n```cs\npublic class UnityConstructorInjectionResolver : IConstructorInjectionResolver\n{\n    private readonly IUnityContainer container;\n\n    public UnityConstructorInjectionResolver(IUnityContainer container)\n    {\n        if (container is null)\n            throw new ArgumentNullException(nameof(container));\n\n        this.container = container;\n    }\n\n    public object Resolve(ParameterInfo parameter)\n    {\n        return container.Resolve(parameter.ParameterType);\n    }\n}\n```\n\nDefine the `options` and create the proxy\n\n```cs\nIUnityContainer container = new UnityContainer();\ncontainer.RegisterType\u003cIMyService, MyService\u003e();\n\nvar options = new ProxyGeneratorOptions();\noptions.ConstructorInjectionResolver = new UnityConstructorInjectionResolver(container);\n\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e(options);\n\nproxy.Method();\n```\n\n_Note: By default the first constructor of the proxied class is selected. Create a custom ConstructorSelector (and set the option) to change this behavior._\n\n\n## ObjectFactory\n\n\u003e Allows to **inject dependencies** in **interceptors** used with interceptor **attributes**\n\n**Sample 1:** Change the default **factory** with an **IoC Container**\n\n```cs\nvar generator = new ProxyGenerator();\n\n// Example with Unity Container\nIUnityContainer container = new UnityContainer();\n\n// here\nObjectFactory.SetDefaultFactory(type =\u003e container.Resolve(type));\n\ncontainer.RegisterType\u003cIService1, Service1\u003e();\ncontainer.RegisterType\u003cIService2, Service2\u003e();\n\netc.\n```\nCreate an interceptor with injections.\n\n```cs\npublic class MyInterceptor : IInterceptor\n{\n    public MyInterceptor(IService1 service1, IService2 service2)\n    {\n        Service1 = service1;\n        Service2 = service2;\n    }\n\n    public IService1 Service1 { get; }\n    public IService2 Service2 { get; }\n\n    public void Intercept(IInvocation invocation)\n    {\n        invocation.Proceed();\n    }\n}\n```\n\n**Sample 2:** get the **same instance** of the **interceptor**. Usefull for **Interface Proxy without target**.\n\n\n```cs\nvar container = new UnityContainer();\n\ncontainer.RegisterType\u003cMyInterceptor\u003e(new ContainerControlledLifetimeManager());\n\nObjectFactory.SetDefaultFactory(type =\u003e container.Resolve(type));\nvar generator = new ProxyGenerator();\n\nvar proxy = generator.CreateInterfaceProxyWithoutTarget\u003cIMyClass\u003e();\n\nproxy.Title = \"New Value\";\nvar title = proxy.Title;\n```\n\n```cs\npublic class MyInterceptor : IInterceptor\n{\n    private string _title;\n\n    public void Intercept(IInvocation invocation)\n    {\n        if (invocation.CallerMethod.Name.StartsWith(\"set_\"))\n            _title= invocation.GetParameter\u003cstring\u003e(0); \n        else\n            invocation.ReturnValue = _title;\n    }\n}\n\npublic interface IMyClass\n{\n    [GetSetTitle]\n    string Title { get; set; }\n}\n\npublic class GetSetTitleAttribute : Attribute, \n    IGetterInterceptorProvider, \n    ISetterInterceptorProvider\n{\n    public Type InterceptorType =\u003e typeof(MyInterceptor);\n}\n```\n\n## Mixins\n\n\u003e Allows to add features to proxy generated.\n\n**Sample** add **INotifyPropertyChanged** to Proxy created\n\n```cs\npublic class MainWindowViewModel\n{\n    private DelegateCommand updateTitleCommand;\n\n    public MainWindowViewModel()\n    {\n        Title = \"Main title\";\n    }\n\n    [PropertyChanged]\n    public virtual string Title { get; set; }\n\n    public DelegateCommand UpdateTitleCommand\n    {\n        get\n        {\n            if (updateTitleCommand == null)\n                updateTitleCommand = new DelegateCommand(ExecuteUpdateTitleCommand);\n            return updateTitleCommand;\n        }\n    }\n\n    protected virtual void ExecuteUpdateTitleCommand()\n    {\n        Title += \"!\";\n    }\n}\n```\n\nDefine the **Mixin**\n\n```cs\npublic interface INotifyPropertyChangedMixin : INotifyPropertyChanged\n{\n    void OnPropertyChanged(object target, string propertyName);\n}\n\npublic class PropertyChangedMixin : INotifyPropertyChangedMixin\n{\n    public void OnPropertyChanged(object target, string propertyName)\n    {\n        PropertyChanged?.Invoke(target, new PropertyChangedEventArgs(propertyName));\n    }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}\n```\n\nDefine an attribute and an **interceptor**\n\n```cs\n[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property)]\npublic class PropertyChangedAttribute : Attribute, ISetterInterceptorProvider\n{\n    public Type InterceptorType =\u003e typeof(PropertyChangedInterceptor);\n}\n\npublic class PropertyChangedInterceptor : Interceptor\n{\n    protected override void OnEnter(IInvocation invocation) { }\n    protected override void OnException(IInvocation invocation, Exception exception)\n    {\n        Console.WriteLine($\"Error: {exception.Message}\");\n    }\n\n    protected override void OnExit(IInvocation invocation)\n    {\n        INotifyPropertyChangedMixin mixin = invocation.Proxy as INotifyPropertyChangedMixin;\n        if (mixin != null)\n        {\n            string propertyName = invocation.Member.Name;\n            mixin.OnPropertyChanged(invocation.Proxy, propertyName);\n        }\n    }\n}\n```\n\nDefine the **options**\n\n```cs\nvar options = new ProxyGeneratorOptions();\noptions.AddMixinInstance(new PropertyChangedMixin());\nvar proxy = generator.CreateClassProxy\u003cMainWindowViewModel\u003e(options);\n```\n\n_Note: **caution** for **proxies** with **target**. We leave the proxy after calling a target member_\n\n\n## Additional Code \n\n\u003e Allows to **customize the code generated**. It's an \"alternative\" to Mixins. Require to write **il code** with [System.Reflection.Emit](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit?view=netcore-3.1).\n\nMethods \n\n* **BeforeDefine**: called just after the TypeBuilder for the Proxy is defined.\n* **AfterDefine**: called after all members defined.\n* **BeforeInvoke**: called before invoke the method on the target or the base class.\n* **AfterInvoke**: called after invoke the method\n\n_Note: the **ProxyScope** allows to find field, property, method, event builders._\n\n**Sample:**\n\n```cs\npublic class MyAdditionalCode : AdditionalCode\n{\n    private static readonly MethodInfo WriteLineMethod = typeof(Console).GetMethod(\"WriteLine\", new Type[] { typeof(string) });\n\n    public override void BeforeInvoke(ProxyScope proxyScope, ILGenerator il, CallbackMethodDefinition callbackMethodDefinition)\n    {\n        il.Emit(OpCodes.Ldstr, $\"Before {callbackMethodDefinition.Method.Name}\");\n        il.Emit(OpCodes.Call, WriteLineMethod);\n    }\n\n    public override void AfterInvoke(ProxyScope proxyScope, ILGenerator il, CallbackMethodDefinition callbackMethodDefinition)\n    {\n        il.Emit(OpCodes.Ldstr, $\"After {callbackMethodDefinition.Method.Name}\");\n        il.Emit(OpCodes.Call, WriteLineMethod);\n    }\n}\n```\n\nCreate a simple  **class**\n\n```cs\npublic class MyClass\n{\n    public virtual void MyMethod()\n    {\n        Console.WriteLine(\"In My Method\");\n    }\n}\n```\n\nDefine the **options**\n\n```cs\nvar generator = new ProxyGenerator();\n\nvar options = new ProxyGeneratorOptions();\noptions.AdditionalCode = new MyAdditionalCode();\n\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e(options);\nproxy.MyMethod();\n```\n\n**Output**\n\n```\nBefore MyMethod\nIn My Method\nAfter MyMethod\n```\n\nCode Generated for the callback method:\n\n```cs\npublic void MyMethod_Callback()\n{\n    Console.WriteLine(\"Before MyMethod\");\n    base.MyMethod();\n    Console.WriteLine(\"After MyMethod\");\n}\n```\n\n**Advanced sample**:  Take a look at the **CodeGenerationSample**\n\n_For example allows to implement INotifyPropertyChanged or create the commands for the ViewModel Proxy generated._\n\n```cs\npublic class ViewModelBuilder : AdditionalCode\n{\n    private NotifyPropertyChangedFeature notifyPropertyChangedFeature;\n    private DelegateCommandBuilder delegateCommandBuilder;\n\n    public ViewModelBuilder()\n    {\n        notifyPropertyChangedFeature = new NotifyPropertyChangedFeature();\n        delegateCommandBuilder = new DelegateCommandBuilder();\n    }\n\n    public override void BeforeDefine(ProxyScope proxyScope)\n    {\n        if (ViewModelSupport.IsViewModel(proxyScope.TypeDefinition.Type))\n        {\n            notifyPropertyChangedFeature.ImplementFeature(proxyScope);\n\n            var commands = ViewModelSupport.GetCommandInfos(proxyScope.TypeDefinition.Type);\n            delegateCommandBuilder.CreateCommands(proxyScope, commands);\n        }\n    }\n\n    public override void BeforeInvoke(ProxyScope proxyScope, ILGenerator il, CallbackMethodDefinition callbackMethodDefinition)\n    {\n        // never called with clean get and set method builder\n\n        if (callbackMethodDefinition.MethodDefinition.MethodDefinitionType == MethodDefinitionType.Setter \u0026\u0026 ViewModelSupport.IsViewModel(proxyScope.TypeDefinition.Type))\n        {\n            notifyPropertyChangedFeature.CheckEquals(proxyScope, il, callbackMethodDefinition.Method);\n        }\n    }\n\n    public override void AfterInvoke(ProxyScope proxyScope, ILGenerator il, CallbackMethodDefinition callbackMethodDefinition)\n    {\n        // never called with clean get and set method builder\n\n        if (callbackMethodDefinition.MethodDefinition.MethodDefinitionType == MethodDefinitionType.Setter \u0026\u0026 ViewModelSupport.IsViewModel(proxyScope.TypeDefinition.Type))\n        {\n            notifyPropertyChangedFeature.InvokeOnPropertyChanged(proxyScope, il, callbackMethodDefinition.Method);\n\n            string propertyName = (callbackMethodDefinition.MethodDefinition as SetMethodDefinition).PropertyDefinition.Name;\n            delegateCommandBuilder.RaiseCanExecuteChangedFor(proxyScope, il, propertyName);\n        }\n    }\n}\n```\n\nViewModel \n\n```cs\n[ViewModel]\npublic class MainWindowViewModel\n{\n    public MainWindowViewModel()\n    {\n        Title = \"The title\";\n    }\n    \n    // The proxy implements INotifyPropertyChanged \n    // The properties raise On PropertyChanged\n    public virtual string Title { get; set; }\n\n    public virtual bool CanExecute { get; set; }\n    \n    // A (Prism) DelegateCommand \"UpdateTitleCommand\" with CanExecute Method is created\n    // CanExecute property raises CanExecuteChanged\n    [Command(\"UpdateTitleCommand\", CanExecuteMethodName = nameof(CanExecuteUpdateTitle), CanExecutePropertyNames = new[] { nameof(CanExecute) })]\n    protected void ExecuteUpdateTitleCommand()\n    {\n        Title += \"!\";\n    }\n\n    protected bool CanExecuteUpdateTitle()\n    {\n        return CanExecute; \n    }\n}\n```\n\n_Another alternative is to create a **custom InterceptableMethodBuilder**. Update the proxy and implement INotifyPropertyChanged (add INotifyPropertyChanged interface, event and protected method to raise the event)  and call OnPropertyChanged method in properties._\n\n\n## Save The Assembly (.NET Framework Only)\n\n```cs\nvar generator = new ProxyGenerator(new ModuleScope(true)); // save\n// with strong name\n// var generator = new ProxyGenerator(new ModuleScope(true, true)); // save, strong name\n// Or change the assembly and module names\n// var generator = new ProxyGenerator(new ModuleScope(\"MyAssembly\",\"MyModule\",true, true)); \n\nvar proxy = generator.CreateClassProxy\u003cMyClass\u003e();\n\n// save the assembly\ngenerator.ModuleScope.Save();\n```\n\nRecommendations: \n\n* Use this feature only for Debug\n* Decompiler : [ILSpy](https://github.com/icsharpcode/ILSpy) (IL with C# comments, not fail to decompile unlike JustDecompile)\n\n## Reflection \n\n* allows to access to private/public/static members (fields, properties, methods, constructors, events accessors)\n* invoke usefull methods\n* intercept get, set, add, remove and methods\n\nSample:\n\n```cs\nvar target = new MyClass();\nvar accessor = new TypeAccessor(target);\naccessor.Properties[\"MyProperty\"].InterceptSet(new object[] { \"New Value\" }, new LogInterceptor());\n```\n\n## Support\n\n* .NET Framework 4.5 and 4.7.2\n* .NET Core 3.1\n* .NET Standard 2.0\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromagny13%2Fnintercept","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromagny13%2Fnintercept","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromagny13%2Fnintercept/lists"}