Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classware: Middleware for classes #12

Open
Arlodotexe opened this issue Aug 9, 2023 · 1 comment
Open

Classware: Middleware for classes #12

Arlodotexe opened this issue Aug 9, 2023 · 1 comment
Labels
high difficulty This will be relatively difficult to do needs research Further information is requested

Comments

@Arlodotexe
Copy link
Owner

Arlodotexe commented Aug 9, 2023

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

  • 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

  1. Identify classes/interfaces marked for plugin generation.
  2. Generate a derived "plugin" for each, implementing both T and IDelegable<T>.
  3. The plugin accepts a T and an IDelegable<T> instance during construction.
  4. Developers can extend the generated base class to craft custom plugins.
  5. Non-overridden plugin members delegate to IDelegable<T>.Inner.
  6. Use base or Inner to invoke the next plugin or the base class.
  7. Generate a Plugin Container class that holds all the ChainedProxyBuilder instances for possible plugins.
  8. 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.

Sources

Example

Usage:

// 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();
    }
}
@Arlodotexe
Copy link
Owner Author

Since this is basically just middleware for classes, I'll be calling this OwlCore.Classware.

@Arlodotexe Arlodotexe transferred this issue from Arlodotexe/brain-dump Feb 12, 2024
@Arlodotexe Arlodotexe added high difficulty This will be relatively difficult to do needs research Further information is requested labels Feb 12, 2024
@Arlodotexe Arlodotexe changed the title Source Generator for Dynamic Behavior Injection Classware: Middleware for classes Mar 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
high difficulty This will be relatively difficult to do needs research Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant