ProxyGen.NET 9.1.0

dotnet add package ProxyGen.NET --version 9.1.0
NuGet\Install-Package ProxyGen.NET -Version 9.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ProxyGen.NET" Version="9.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ProxyGen.NET --version 9.1.0
#r "nuget: ProxyGen.NET, 9.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ProxyGen.NET as a Cake Addin
#addin nuget:?package=ProxyGen.NET&version=9.1.0

// Install ProxyGen.NET as a Cake Tool
#tool nuget:?package=ProxyGen.NET&version=9.1.0

ProxyGen.NET Build status AppVeyor tests Coverage Status Nuget (with prereleases) GitHub last commit (branch)

.NET proxy generator powered by Roslyn

This documentation refers the version 9.X of the library


This library currently supports generating proxies for interface interception and duck typing.

To hook into interface method calls:

  1. Create the interceptor class (which is an InterfaceInterceptor descendant):
using Solti.Utils.Proxy;
public class MyInterceptor: InterfaceInterceptor<IMyInterface>
  public MyInterceptor(IMyInterface target) : base(target) {}

  public MyInterceptor(IMyInterface target, MyParam myParam) : base(target) {}  // overloaded constructor

  public override object? Invoke(InvocationContext context) // Invoking the generated proxy instance will trigger this method
	  if (suppressOriginalMethod)
	    return something;
      // ref|out parameters can be assigned by setting the corresponding "context.Args[]" item 
	  context.Args[0] = someNewVal; // "someNewVal" will be forwarded to the original method
	  return base.Invoke(context); // Let the original method do its work
// OR
public class MyInterceptorTargetingTheImplementation: InterfaceInterceptor<IMyInterface, MyInterfaceImplementation>
    public MyInterceptor(MyInterfaceImplementation target) : base(target) {}

    public override object? Invoke(InvocationContext context)
            ifaceMember  = context.InterfaceMember,  // Will point to the invoked IMyInterface member (e.g.: IMyInterface.Foo())
            targetMember = context.TargetMember; // Will point to the underlying MyInterfaceImplementation member (e.g. MyInterfaceImplementation.Foo())

        return base.Invoke(context);
  1. Generate a proxy instance invoking the desired constructor:
using System;
IMyInterface target = new MyClass();
IMyInterface proxy;

proxy = ProxyGenerator<IMyInterface, MyInterceptor>.Activate(Tuple.Create(target)); // or ActivateAsync()
proxy = ProxyGenerator<IMyInterface, MyInterceptor>.Activate(Tuple.Create(target, new MyParam()));
  1. Enjoy


  • The target can access its most outer enclosing proxy. To achieve this it just has to implement the IProxyAccess<IMyInterface> interface:
    using Solti.Utils.Proxy;
    public class MyClass : IMyInterface, IProxyAccess<IMyInterface>
        public IMyInterface Proxy { get; set; }
  • Starting from v9.1 partial interface implementations are also supported:
    using Solti.Utils.Proxy;
    public interface IMyInterface
        void Intercepted();
        void NotInterceptred();
    public class MyInterceptor: InterfaceInterceptor<IMyInterface>
        public void NotInterceptred() {...}
        // will be triggered by Intercepted() only as NotInterceptred() has it's own implementation
        public override object Invoke(InvocationContext context) {...}

For further usage examples see this or that.

To create ducks:

  1. Declare an interface that covers all the desired members of the target class:
public class TargetClass // does not implement IDuck
  public void Foo() {...}
public interface IDuck 
  void Foo();
  1. Generate the duck instance:
using Solti.Utils.Proxy.Generators;
TargetClass target = ...;
IDuck duck = DuckGenerator<IDuck, TargetClass>.Activate(Tuple.Create(target)); // or ActivateAsync()
  1. Quack

Related tests can be seen here.

Caching the generated assembly

By setting the ProxyGen.AssemblyCacheDir property in YourApp.runtimeconfig.json you can make the system cache the generated assembly, so next time your app starts and requests the proxy there won't be time consuming emitting operation.

You can do it easily by creating a template file named runtimeconfig.template.json in your project folder:

  "configProperties": {
    "ProxyGen.AssemblyCacheDir": "GeneratedAssemblies"

Embedding the generated type

This library can be used as a source generator so you can embed the generated proxy type into the assembly that uses it. This is simply done by the Solti.Utils.Proxy.Attributes.EmbedGeneratedTypeAttribute:

[assembly: EmbedGeneratedType(typeof(ProxyGenerator<IMyInterface, MyInterceptor<IMyInterface>>))]
[assembly: EmbedGeneratedType(typeof(DuckGenerator<IMyInterface, MyClass>))]

The xXxGenerator.GetGeneratedType() method returns the embedded type if it is present in the assembly in which the GetGeneratedType() was called. Since all the time consumig operations already happened in compile time, requesting embedded types can singificantly improve the performance.

Note that:

  • Open generics are not supported.
  • (and other coverage reporters) may crash if your project was augmented by a source generator. To work this issue around:
    • Ignore the generated sources in your coverage app (e.g.: in OpenCover use the -filter:-[*]Proxies.GeneratedClass_* switch)
    • Create an empty file for each generated class (e.g.: YourProject\Solti.Utils.Proxy\Solti.Utils.Proxy.Internals.ProxyEmbedder\Proxies.GeneratedClass_XxX.cs)
    • Exclude these files from your project:
      <Compile Remove="Solti.Utils.Proxy\**" />
      <EmbeddedResource Remove="Solti.Utils.Proxy\**" />
      <None Remove="Solti.Utils.Proxy\**" />

Inspecting the generated code

ProxyGen is able to dump the generated sources. Due to performance considerations it is disabled by default. To enable

  • In runtime:

    Set the ProxyGen.SourceDump property (in the same way you could see above) to the desired directory (note that environment variables are supported):

      "configProperties": {
        "ProxyGen.SourceDump": "%TEMP%"
  • In compile time (source generator):

    Extend your .csproj with the following:


The output should look like this.

Migrating from version

  • 2.X
    • Delete all the cached assemblies (if the [Proxy|Duck]Generator.CacheDirectory is set somewhere)
    • InterfaceInterceptor.Invoke() returns the result of the original method (instead of CALL_TARGET) so in the override you may never need to invoke the method parameter directly.
  • 3.X
    • [Proxy|Duck]Generator.GeneratedType[Async] property has been removed. To get the generated proxy type call the [Proxy|Duck]Generator.GetGeneratedType[Async]() method.
    • [Proxy|Duck]Generator.CacheDirectory property has been removed. To set the cache directory tweak the runtimeconfig.json file.
  • 4.X
    • The layout of the InterfaceInterceptor<>.Invoke() has been changed. Invocation parameters can be grabbed from the InvocationContext passed to the Invoke() method.
    • The ConcurrentInterfaceInterceptor<> class has been dropped since the InterfaceInterceptor<> class was rewritten in a thread safe manner.
  • 5.X
    • You don't need to manually activate the generated proxy type, instead you may use the built-in Generator.Activate() method.
  • 6.X
    • The InvocationContext.InvokeTarget property has been removed but you should not be affected by it
    • As proxy embedder has been reimplemented using the v2 Source Generator API, this feature now requires VS 2022
  • 7.X
    • InterfaceInterceptor<TInterface>.Member|Method has been renamed to InterfaceMember|InterfaceMethod
  • 8.X
    • Generators have been demoted to class. To compare Generator instances use their Id property.


Supported frameworks

This project currently targets netstandard2.0 as well as netstandard2.1 and had been tested against net472, netcoreapp3.1, net5.0, net6.0, net7.0 and net8.0.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on ProxyGen.NET:

Package Downloads

A featherweight dependency injector.


OrmLite extensions


Lightweight client to invoke RPC services built with RPC.NET

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
9.1.0 351 12/28/2023
9.1.0-RoslynV3 84 12/28/2023
9.0.0 192 12/9/2023
9.0.0-RoslynV3 92 12/9/2023
8.2.1 278 5/29/2023
8.2.1-RoslynV3 95 5/29/2023
8.2.0 392 3/12/2023
8.2.0-RoslynV3 103 3/12/2023
8.1.1 243 2/26/2023
8.1.0 508 2/12/2023
8.0.1 398 1/22/2023
8.0.0 511 12/21/2022
8.0.0-preview1 110 11/23/2022
7.1.0 393 11/5/2022
7.0.0 741 8/30/2022
7.0.0-preview6 265 6/4/2022
7.0.0-preview5 225 5/20/2022
7.0.0-preview4 184 5/11/2022
7.0.0-preview3 153 4/28/2022
7.0.0-preview2 142 4/26/2022
7.0.0-preview1 171 3/19/2022
6.0.1 1,200 3/16/2022
6.0.0 1,111 3/13/2022
6.0.0-preview2 140 3/13/2022
6.0.0-preview1 136 3/5/2022
5.0.1 2,181 12/12/2021
5.0.0 1,917 7/19/2021
4.0.2 1,618 4/14/2021
4.0.1 2,234 3/17/2021
4.0.0 884 3/13/2021
4.0.0-preview8 928 1/27/2021
4.0.0-preview7 247 1/24/2021
4.0.0-preview6 187 1/23/2021
4.0.0-preview5 219 1/22/2021
4.0.0-preview4 214 1/22/2021
4.0.0-preview3 237 1/17/2021
4.0.0-preview2 205 1/14/2021
4.0.0-preview1 301 11/17/2020
3.1.4 1,113 10/7/2020
3.1.3 1,186 9/25/2020
3.1.2 417 9/24/2020
3.1.1 659 9/8/2020
3.1.0 435 8/31/2020
3.0.3 479 8/27/2020
3.0.2 3,059 7/9/2020
3.0.1 409 7/6/2020
3.0.0 785 6/15/2020
2.1.1 727 6/2/2020
2.1.0 439 6/2/2020
2.0.3 961 5/21/2020
2.0.2 442 5/19/2020
2.0.1 414 5/19/2020
2.0.0 1,117 4/2/2020
1.1.1 1,745 1/31/2020
1.1.0 859 1/23/2020
1.0.0 637 1/18/2020
1.0.0-preview2 414 1/14/2020
1.0.0-preview1 325 1/8/2020