Introduce a source generator to facilitate compile-time dynamic behavior injection. This generator will produce "plugin" implementations for classes or interfaces, allowing for behavior extension and customization without altering the original code.
Inspiration
This concept is inspired by the Strix Music SDK's "model plugin" system, which lets developers enhance the SDK by wrapping around and overriding SDK model members. Our goal is to generalize this idea for any class or interface.
Key Features
Plugin Generation: Automatically derive a "plugin" for any class or interface.
Delegation Mechanism: Utilize IDelegable<T> to delegate to other T instances when members aren't overridden.
Fallback Strategy: Non-overridden members default to IDelegable<T>.Inner.
Plugin Chaining: Chain multiple plugins, replacing the original object upon execution.
Interface Plugins: Apply interface plugins to any class inheriting the interface.
Generated Plugin Container: A container class that holds all the ChainedProxyBuilder instances for possible plugins.
Generated Wrapper Class: For each concrete class, a wrapper class is generated that applies the relevant plugins and delegates calls to the enhanced instance.
Workflow
Identify classes/interfaces marked for plugin generation.
Generate a derived "plugin" for each, implementing both T and IDelegable<T>.
The plugin accepts a T and an IDelegable<T> instance during construction.
Developers can extend the generated base class to craft custom plugins.
Non-overridden plugin members delegate to IDelegable<T>.Inner.
Use base or Inner to invoke the next plugin or the base class.
Generate a Plugin Container class that holds all the ChainedProxyBuilder instances for possible plugins.
Generate a Wrapper class for each concrete class that uses the Plugin Container to apply the relevant plugins and delegates calls to the enhanced instance.
Applications
RPC Library: Generate proxy implementations for interfaces, enabling a Remote Procedure Call library for distant method calls.
Dynamic Behavior: Craft plugins for behavior modification, logging, caching, validation, etc.
Modularity: Promote a modular design where core functionalities stem from base classes/interfaces, and plugins offer additional features.
Conclusion
This source generator promises a robust method to extend and customize class/interface behavior during compile-time. By crafting "plugin" implementations that can encapsulate and override originals, we're paving the way for a modular and scalable software architecture.
// Unsealed class, original implementation.
public class MyClass : SomeBase, ISomething, ISomethingElse
{
public void DoSomething() { }
public void MyMethod() { }
}
// Custom plugin derived from the generated plugin base class.
// `Inner` may be the original implementation, a plugin, or a collapsed chain of plugins.
public partial class LoggingPlugin : SomethingPlugin
{
public LoggingPlugin(ISomething inner) : base(inner) { }
public override void DoSomething()
{
Console.WriteLine("Before DoSomething");
base.DoSomething();
Console.WriteLine("After DoSomething");
}
}
// Use the source generator to request a plugin and trigger Wrapper generation
MyClass original = new();
MyClass wrapped = original.CreatePlugin(plugins: x => new LoggingPlugin(x));
Generated code:
// Generated plugin base class (by the source generator)
// Generate virtual call delegation for all members in ISomething.
public partial class SomethingPlugin : ISomething, IDelegable<ISomething>
{
protected ISomething Inner { get; }
public SomethingPlugin(ISomething inner)
{
Inner = inner;
}
public virtual void DoSomething()
{
Inner.DoSomething();
}
}
// Generated plugin container (by the source generator)
public class MyClassPluginContainer
{
public ChainedProxyBuilder<ISomething> SomethingPlugins { get; } = new ChainedProxyBuilder<ISomething>();
// ... other ChainedProxyBuilders for other interfaces and base class
}
// Generated plugin wrapper class (by the source generator)
public partial class MyClassWrapper : MyClass
{
private readonly MyClass _inner;
private readonly ISomething _innerSomething;
public MyClassWrapper(MyClass inner, MyClassPluginContainer pluginContainer)
{
_inner = inner;
_innerSomething = pluginContainer.SomethingPlugins.Execute(inner);
// ... apply other plugins from the container
}
public override void DoSomething()
{
_innerSomething.DoSomething();
}
public override void MyMethod()
{
_inner.MyMethod();
}
}
Overview
Introduce a source generator to facilitate compile-time dynamic behavior injection. This generator will produce "plugin" implementations for classes or interfaces, allowing for behavior extension and customization without altering the original code.
Inspiration
This concept is inspired by the Strix Music SDK's "model plugin" system, which lets developers enhance the SDK by wrapping around and overriding SDK model members. Our goal is to generalize this idea for any class or interface.
Key Features
IDelegable<T>
to delegate to otherT
instances when members aren't overridden.IDelegable<T>.Inner
.Workflow
T
andIDelegable<T>
.T
and anIDelegable<T>
instance during construction.IDelegable<T>.Inner
.base
orInner
to invoke the next plugin or the base class.Applications
Conclusion
This source generator promises a robust method to extend and customize class/interface behavior during compile-time. By crafting "plugin" implementations that can encapsulate and override originals, we're paving the way for a modular and scalable software architecture.
Sources
StrixMusic.Sdk.Plugins.Model
referenceExample
Usage:
Generated code: