Skip to content

OutWit.Common.Proxy.Generator

Dmitry Ratner edited this page Jun 30, 2025 · 1 revision

Introduction to OutWit.Common.Proxy.Generator

OutWit.Common.Proxy.Generator is a C# Source Generator that automatically creates proxy classes for interfaces decorated with [ProxyTarget] attribute. This generator enables compile-time interception of methods, properties, and events, eliminating runtime reflection for proxy creation.

Generator Internals

Step 1: Finding Candidate Interfaces

The generation process begins in the ServiceProxyGenerator.Execute method.

  1. The generator calls the GetCandidates() extension method on the current Compilation object.
  2. This helper method scans all types in the assembly being compiled, as well as all referenced assemblies.
  3. It filters this list to find only interfaces that are decorated with the ProxyTargetAttribute.

Step 2: Generating the Class Shell

For each interface found, the generator begins to construct the proxy class source code using a StringBuilder.

  1. Determine Class Name: The generator checks the ProxyTargetAttribute for a constructor argument. If a name is provided (e.g., [ProxyTarget("MyProxy")]), it uses that name. Otherwise, it defaults to [InterfaceName]Proxy.
  2. Create Boilerplate: It generates the namespace, the class definition (public class MyProxy : IMyInterface), a private readonly field for the IProxyInterceptor, and a public constructor to initialize it.

Step 3: Processing Members

[cite_start]The generator retrieves a unique set of all members from the target interface, including any members inherited from base interfaces, by using the GetAllMembers helper method. [cite: 150]

It then iterates through each member and uses specialized helper generators based on the member's type:

  • IMethodSymbol is handled by MethodGenerator.
  • IPropertySymbol is handled by PropertyGenerator.
  • IEventSymbol is handled by EventGenerator.

Step 4: Member Implementation Details

Each specialized generator implements a specific pattern.

Methods (MethodGenerator)

  • It ignores methods that are part of properties or events.
  • It creates a new ProxyInvocation object.
  • It populates the ProxyInvocation instance with all relevant metadata: MethodName, Parameters, ParametersTypes, ReturnType, and boolean flags like HasReturnValue, ReturnsTask, and ReturnsTaskWithResult.
  • It generates a call to _interceptor.Intercept(invocation).
  • Finally, it generates the return logic, with special handling for returning Task, Task<T>, or a simple value from invocation.ReturnValue.

Properties (PropertyGenerator)

  • It generates get and set accessors if they exist on the interface property.
  • The get accessor creates an invocation with MethodName = "get_PropertyName" and returns the result from invocation.ReturnValue.
  • The set accessor creates an invocation with MethodName = "set_PropertyName" and passes the incoming value as the only parameter.

Events (EventGenerator)

  • It generates add and remove accessors for the event.
  • The add accessor creates an invocation with MethodName = "add_EventName", calls the interceptor, and then subscribes the handler to a private backing field (_{eventName} += value;).
  • The remove accessor does the same for remove_EventName and unsubscribes from the backing field (_{eventName} -= value;).

Step 5: Finalizing the Source

Once the StringBuilder contains the full source code for the class, the generator adds it to the compilation via context.AddSource(). The new file is named based on the generated class name (e.g., MyProxy.g.cs).

Debugging the Generator

You may occasionally need to debug the source generator itself, for instance, if it crashes during compilation or produces incorrect code.

When to Debug?

  • The compilation fails with an error originating from your generator.
  • The generated .g.cs file contains syntax errors or incorrect logic.
  • You are developing a new feature in the generator and want to step through its execution.

How to Debug the Generator

The process involves attaching a debugger to the C# compiler process.

  1. Add Debugger Launch Code: Temporarily add the following line to the beginning of the Execute method in your generator (ServiceProxyGenerator.cs):

    public void Execute(GeneratorExecutionContext context)
    {
        System.Diagnostics.Debugger.Launch(); // This line will launch the debugger
        
        // ... rest of your generator code ...
    }
  2. Rebuild the Generator Project: Build the OutWit.Common.Proxy.Generator project to apply the changes.

  3. Trigger a Build of the Consuming Project: In Visual Studio, open a project that references your generator and start a build (Build -> Build Solution).

  4. Attach the Debugger: When the compiler executes your generator, a dialog window will appear asking you to select a debugger.

    • Choose your current Visual Studio instance or start a new one.
  5. Start Debugging: After attaching, execution will break on the Debugger.Launch() line. You can now set breakpoints, inspect variables (like the context object), and step through your generator's code as if it were a normal application.