-
Notifications
You must be signed in to change notification settings - Fork 0
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.
The generation process begins in the ServiceProxyGenerator.Execute method.
- The generator calls the
GetCandidates()extension method on the currentCompilationobject. - This helper method scans all types in the assembly being compiled, as well as all referenced assemblies.
- It filters this list to find only interfaces that are decorated with the
ProxyTargetAttribute.
For each interface found, the generator begins to construct the proxy class source code using a StringBuilder.
-
Determine Class Name: The generator checks the
ProxyTargetAttributefor a constructor argument. If a name is provided (e.g.,[ProxyTarget("MyProxy")]), it uses that name. Otherwise, it defaults to[InterfaceName]Proxy. -
Create Boilerplate: It generates the namespace, the class definition (
public class MyProxy : IMyInterface), a private readonly field for theIProxyInterceptor, and a public constructor to initialize it.
[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:
-
IMethodSymbolis handled byMethodGenerator. -
IPropertySymbolis handled byPropertyGenerator. -
IEventSymbolis handled byEventGenerator.
Each specialized generator implements a specific pattern.
- It ignores methods that are part of properties or events.
- It creates a new
ProxyInvocationobject. - It populates the
ProxyInvocationinstance with all relevant metadata:MethodName,Parameters,ParametersTypes,ReturnType, and boolean flags likeHasReturnValue,ReturnsTask, andReturnsTaskWithResult. - 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 frominvocation.ReturnValue.
- It generates
getandsetaccessors if they exist on the interface property. - The
getaccessor creates an invocation withMethodName = "get_PropertyName"and returns the result frominvocation.ReturnValue. - The
setaccessor creates an invocation withMethodName = "set_PropertyName"and passes the incomingvalueas the only parameter.
- It generates
addandremoveaccessors for the event. - The
addaccessor creates an invocation withMethodName = "add_EventName", calls the interceptor, and then subscribes the handler to a private backing field (_{eventName} += value;). - The
removeaccessor does the same forremove_EventNameand unsubscribes from the backing field (_{eventName} -= value;).
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).
You may occasionally need to debug the source generator itself, for instance, if it crashes during compilation or produces incorrect code.
- The compilation fails with an error originating from your generator.
- The generated
.g.csfile contains syntax errors or incorrect logic. - You are developing a new feature in the generator and want to step through its execution.
The process involves attaching a debugger to the C# compiler process.
-
Add Debugger Launch Code: Temporarily add the following line to the beginning of the
Executemethod 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 ... }
-
Rebuild the Generator Project: Build the
OutWit.Common.Proxy.Generatorproject to apply the changes. -
Trigger a Build of the Consuming Project: In Visual Studio, open a project that references your generator and start a build (
Build -> Build Solution). -
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.
-
Start Debugging: After attaching, execution will break on the
Debugger.Launch()line. You can now set breakpoints, inspect variables (like thecontextobject), and step through your generator's code as if it were a normal application.