Skip to content

Interceptors and Proxies: Diagrams

Diagrams for the concepts introduced in Interceptors and Proxies.

Proxy Call Flow

How a method call flows through the proxy system:

Proxy Generation at Compile Time

Proxy Type Hierarchy

Generated Proxy Fields

FieldTypePurpose
__interceptorInterceptorThe interceptor instance
__cachedIntercepted0Func<ArgumentList, Task<string>>Cached delegate to target
__cachedIntercept0Func<Invocation, Task<string>>Cached intercept delegate
ProxyTargetobject?Real service (from InterfaceProxy)

Invocation Structure

FieldDescription
ProxyThe proxy instance (e.g., IGreetingServiceProxy)
MethodMethodInfo of the called method
ArgumentsArgumentList containing method arguments
InterceptedDelegateDelegate to call the real implementation (for pass-through)
InterfaceProxyTargetThe real service instance

ArgumentList Variants

CountGeneric TypeSimple Type
0ArgumentList0ArgumentList0
1ArgumentListG1<>ArgumentListS1
2ArgumentListG2<,>ArgumentListS2
3ArgumentListG3<,,>ArgumentListS3
4ArgumentListG4<,,,>ArgumentListS4
5-10(uses Simple)ArgumentListS5-S10

ArgumentList Methods

MethodDescription
.Get<T>(index)Get argument at index
.GetCancellationToken(index)Get cancellation token
.Set<T>(index, val)Set argument value
.LengthNumber of arguments

Handler Caching

Interceptor Chain

Multiple interceptors can be chained together:

Pass-Through vs Virtual Proxy

AspectPass-Through ProxyVirtual Proxy
CreationProxies.New(typeof(IService), interceptor, proxyTarget: realService)Proxies.New(typeof(IService), interceptor)
ProxyTarget!= null== null
FlowProxy → Interceptor → InvokeIntercepted() → Real ServiceProxy → Interceptor → Return default/mock
Use casesLogging, Metrics, Caching, Retry logicMocking/Stubs, Default values, RPC client proxies, Lazy initialization

Typed vs Untyped Handlers

AspectTyped Handlers (Default)Untyped Handlers
MethodCreateTypedHandler<TUnwrapped>(invocation, methodDef)CreateUntypedHandler(invocation, methodDef)
SetupDefault behaviorUsesUntypedHandlers = true in constructor
Return typeTUnwrapped (e.g., string for Task<string>)object?
PerformanceOne handler instantiation per unique return typeNo generic instantiation overhead
Use caseMost use casesComputeServiceInterceptor for max performance

MethodDef Key Properties

For Task<string> GreetAsync(string name, CancellationToken ct):

PropertyValue
MethodInfoGreetAsync
FullName"MyNamespace.IGreetingService.GreetAsync"
ReturnTypetypeof(Task<string>)
UnwrappedReturnTypetypeof(string)
IsAsyncMethodtrue
ReturnsTasktrue
ReturnsValueTaskfalse
IsAsyncVoidMethodfalse
CancellationTokenIndex1
Parameters[name: string, ct: CancellationToken]

Helper Methods

MethodDescription
DefaultResultCompleted Task with default(T)
WrapResult(value)Task.FromResult(value)
WrapAsyncInvokerResult(task)Proper Task<T> or ValueTask<T>
InterceptedAsyncInvokerFunc<Invocation, Task<T>>
TargetAsyncInvokerFunc<object, Args, Task<T>>

See Also