{"id":20032990,"url":"https://github.com/schwindelig/wraptor","last_synced_at":"2026-05-13T04:02:52.470Z","repository":{"id":118707867,"uuid":"128634016","full_name":"schwindelig/wraptor","owner":"schwindelig","description":"A simple dynamic proxy targeting .net standard 2.0","archived":false,"fork":false,"pushed_at":"2018-09-20T06:20:08.000Z","size":29,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-23T10:47:20.290Z","etag":null,"topics":["dotnet","dynamic-proxy","emit","il","proxy","standard"],"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/schwindelig.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-04-08T11:28:30.000Z","updated_at":"2024-02-12T21:14:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"5be468f1-3ae4-4256-a46f-226d09d4f9a5","html_url":"https://github.com/schwindelig/wraptor","commit_stats":{"total_commits":28,"total_committers":1,"mean_commits":28.0,"dds":0.0,"last_synced_commit":"9e3d7ce5d17a708dccfecfbc917bcf7ae87f9529"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schwindelig%2Fwraptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schwindelig%2Fwraptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schwindelig%2Fwraptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schwindelig%2Fwraptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schwindelig","download_url":"https://codeload.github.com/schwindelig/wraptor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241465078,"owners_count":19967243,"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":["dotnet","dynamic-proxy","emit","il","proxy","standard"],"created_at":"2024-11-13T09:44:02.392Z","updated_at":"2026-05-13T04:02:52.410Z","avatar_url":"https://github.com/schwindelig.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Wraptor\n\n**A simple dynamic proxy targeting .net standard 2.0**\n\n[![Build status](https://ci.appveyor.com/api/projects/status/nobrrdmb88sdkcht?svg=true)](https://ci.appveyor.com/project/DavidSzke/wraptor) [![NuGet](https://img.shields.io/nuget/v/Wraptor.svg)](https://www.nuget.org/packages/Wraptor/)\n\nWraptor allows you to extend, intercept, modify and redirect calls to interface defined methods on any object. This is achieved by generating a proxy object at run-time that redirects calls to an object of type `IInterceptor`.\n\n`IInterceptor` currently provides three hooks:\n- `void PreInvoke(object implementation, MethodInfo methodInfo, object[] arguments)`\n- `object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)`\n- `void PostInvoke(object implementation, MethodInfo methodInfo, object[] arguments, object returnValue)`\n\nWraptor's IL-Generation and proxy source was inspired by John Mikhail's article [Dynamic Proxy Creation Using C# Emit](https://www.codeproject.com/Articles/5511/Dynamic-Proxy-Creation-Using-C-Emit)\n\n## Getting started\nInstall the nuget package either via GUI in Visual Studio or enter the following command in the package manager console:\n```powershell\nInstall-Package Wraptor\n```\n\nFirst, you need to define an implementation of `Wraptor.IInterceptor`. Wraptor provides a handy `Wraptor.InterceptorBase` class you can extend:\n```csharp\npublic class MyInterceptor : InterceptorBase\n{\n    public override void PreInvoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        Console.WriteLine($\"PreInvoke: {methodInfo.Name} on {implementation.GetType().Name}\");\n        base.PreInvoke(implementation, methodInfo, arguments);\n    }\n\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        Console.WriteLine($\"Invoke: {methodInfo.Name} on {implementation.GetType().Name}\");\n        return base.Invoke(implementation, methodInfo, arguments);\n    }\n\n    public override void PostInvoke(object implementation, MethodInfo methodInfo, object[] arguments, object returnValue)\n    {\n        Console.WriteLine($\"PostInvoke: {methodInfo.Name} on {implementation.GetType().Name}\");\n        base.PostInvoke(implementation, methodInfo, arguments, returnValue);\n    }\n}\n```\nThis implementation can now be used on an instance of the `Wraptor.Generator` class to create the proxy using the `Generate\u003cT\u003e` method:\n```csharp\npublic interface IPerson\n{\n    void SayName();\n}\n\npublic class Person : IPerson\n{\n    public string Name { get; set; }\n\n    public void SayName() =\u003e Console.WriteLine($\"Hi, my name is {this.Name}\");\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var interceptor = new MyInterceptor();\n        var alice = new Person { Name = \"Alice\" };\n        var proxy = generator.Generate\u003cIPerson\u003e(alice, interceptor);\n\n        proxy.SayName();\n    }\n}\n```\nWhich will result in the following output:\n```\nPreInvoke: SayName on Person\nInvoke: SayName on Person\nHi, my name is Alice\nPostInvoke: SayName on Person\n```\nPlease note the explicit definition of `IPerson` in the `Generate\u003cT\u003e` call. Wraptor uses the here defined interface to generate the proxy methods. Defining anything other than an interface will result in an `ArgumentException`.\n\n## Usage examples\n### Measuring a method's execution time\nOne way is to start a `Stopwatch` in the `Invoke` method, wait for the actual method to complete and then process the ellapsed time.\n```csharp\npublic class MeasureInterceptor : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        var stopwatch = Stopwatch.StartNew();\n        var result = base.Invoke(implementation, methodInfo, arguments);\n        Console.WriteLine($\"{methodInfo.Name} took {stopwatch.ElapsedMilliseconds}ms\");\n\n        return result;\n    }\n}\n```\nAnother approach could be to have a timer at instance level of the Interceptor and toggle the measuring in the `PreInvoke` and `PostInvoke` methods.\n### Modifying arguments passed to the method\nThe arguments that will or have been used will be passed to all hooks as boxed objects in an array through the `arguments` parameter. To have an effect on the execution of the method, you will have to modify them in either the `PreInvoke` or `Invoke` method.\n\nThe following example illustrates the change of the first and only parameter passed to the `Deposit` method:\n```csharp\npublic interface IPerson\n{\n    void Deposit(decimal moneys);\n}\n\npublic class Person : IPerson\n{\n    public decimal Balance { get; set; }\n\n    public void Deposit(decimal moneys) =\u003e this.Balance += moneys;\n}\n\npublic class ArgumentInterceptor : InterceptorBase\n{\n    public override void PreInvoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        if (methodInfo.Equals(typeof(IPerson).GetMethod(nameof(IPerson.Deposit))))\n        {\n            arguments[0] = 90001m;\n        }\n    }\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var interceptor = new ArgumentInterceptor();\n        var alice = new Person { Balance = 0 };\n        var proxy = generator.Generate\u003cIPerson\u003e(alice, interceptor);\n\n        proxy.Deposit(42);\n        Console.WriteLine(alice.Balance);\n    }\n}\n```\nWhich then produces the following output:\n```\n90001\n```\n### Modifying the return value\nThe `Invoke` method provides the result of the original method as a boxed object. You can modify this object before returning which will affect every caller:\n```csharp\npublic interface IPerson\n{\n    int Age { get; set; }\n}\n\npublic class Person : IPerson\n{\n    public int Age { get; set; }\n}\n\npublic class ReturnValueInterceptor : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        var result = base.Invoke(implementation, methodInfo, arguments);\n\n        if (methodInfo.Name.Equals($\"get_{nameof(IPerson.Age)}\", StringComparison.InvariantCultureIgnoreCase))\n        {\n            result = (int)result + 17;\n        }\n\n        return result;\n    }\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var interceptor = new ReturnValueInterceptor();\n        var alice = new Person { Age = 25 };\n        var proxy = generator.Generate\u003cIPerson\u003e(alice, interceptor);\n\n        Console.WriteLine(proxy.Age);\n    }\n}\n```\nWhich will result in the following output:\n```\n42\n```\n### Suppressing method invocations\nSince the `Invoke` method controls the flow of execution, the call to the proxied method can be avoided by simply returning early:\n```csharp\npublic interface IProcessor\n{\n    void BroadcastMessage(string message);\n}\n\npublic class Processor : IProcessor\n{\n    public void BroadcastMessage(string message) =\u003e Console.WriteLine(message);\n}\n\npublic class Suppressor : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments) =\u003e null;\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var interceptor = new Suppressor();\n        var processor = new Processor();\n        var proxy = generator.Generate\u003cIProcessor\u003e(processor, interceptor);\n\n        proxy.BroadcastMessage(\"Hello World\");\n    }\n}\n```\nThe example above will not produce any output at all, as the `Console.WriteLine` statement will never be reached.\n\n### Redirecting method invocations\nWe can therefore also redirect calls to completely different methods, as long as the underlying type of the return value is the same as on the originally called method:\n```csharp\npublic interface IProcessor\n{\n    string ProcessMessage(string message);\n}\n\npublic class Processor : IProcessor\n{\n    public string ProcessMessage(string message) =\u003e $\"{message} - normally processed\";\n}\n\npublic class OtherProcessor\n{\n    public string ProcessMessage(string message) =\u003e $\"{message} - processed by {this.GetType().Name}\";\n}\n\npublic class Redirector : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        var otherProcessor = new OtherProcessor();\n        return otherProcessor.ProcessMessage((string)arguments[0]);\n    }\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var interceptor = new Redirector();\n        var processor = new Processor();\n        var proxy = generator.Generate\u003cIProcessor\u003e(processor, interceptor);\n\n        var result = proxy.ProcessMessage(\"Hello World\");\n        Console.WriteLine(result);\n    }\n}\n```\nWhich will result in the following output:\n```\nHello World - processed by OtherProcessor\n```\n### Chaining interceptor\nSince the generated proxies implement the defined interface, it is possible to chain interceptors:\n```csharp\npublic interface IProcessor\n{\n    void ProcessMessage(string message);\n}\n\npublic class Processor : IProcessor\n{\n    public void ProcessMessage(string message) =\u003e Console.WriteLine(message);\n}\n\npublic class InterceptorOne : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        arguments[0] = arguments[0] + \" modified by InterceptorOne\";\n        return base.Invoke(implementation, methodInfo, arguments);\n    }\n}\n\npublic class InterceptorTwo : InterceptorBase\n{\n    public override object Invoke(object implementation, MethodInfo methodInfo, object[] arguments)\n    {\n        arguments[0] = arguments[0] + \" modified by InterceptorTwo\";\n        return base.Invoke(implementation, methodInfo, arguments);\n    }\n}\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        var generator = new Generator();\n        var processor = new Processor();\n        var proxy = generator.Generate\u003cIProcessor\u003e(processor, new InterceptorOne());\n        proxy = generator.Generate\u003cIProcessor\u003e(proxy, new InterceptorTwo());\n\n        proxy.ProcessMessage(\"Hello World\");\n    }\n}\n```\nWhich generates following output:\n```\nHello World modified by InterceptorTwo modified by InterceptorOne\n```\n\n## Roadmap\n- Support for non-interface public methods\n- Interception for private method calls (not sure if this is even possible)\n- Support for extracting and saving generated proxies\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschwindelig%2Fwraptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschwindelig%2Fwraptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschwindelig%2Fwraptor/lists"}