动态组合 类 以消除 API 的责任

Dynamically combine classes in order to remove responsibilities from API

我正在构建一个 API(用于游戏引擎),它公开了两个接口,分别称为 IWindowIEngineWindow

IWindow 接口应该由 API 用户实现,IEngineWindow 接口被引擎用来与 window 交互。

window 对象应该有一个 List<IWindowControl> 类型的私有成员。 我可以使用抽象 class 并摆脱接口,但我会在我的 API 中包含我不想要的实现细节。

我对这个问题的理论解决方案是 API-user 在他自己的 class 中实现 IWindow 并调用一个方法(类似于 GetEngineWindow(typeof(MyWindowClass)))returns一个与 MyWindowClass 的实例相同的对象,除了它还实现了 IEngineWindow 接口。

我计划在 GetEngineWindow() 方法中使用 System.Reflection.Emit 来动态组合 MyWindowClass 与实现 IEngineWindow 接口的内部 class 但我很快意识到这将是它自己的一个主要项目。

我的问题归结为是否有更简单的解决方案来从 API 中删除这种实现细节,或者是否存在一个库(免费用于商业用途)来执行这种 class-融合。

如果我的问题太抽象,这里有一个代码示例,说明我希望能够做什么:

//API (dll-file)
interface IWindow
{
    void BeforeClose();
}

interface IEngineWindow
{
    void Show();
}

//Built into engine (written by me)
class Program
{
    static void Main(string[] args)
    {
        object window = CombineClasses(typeof(Testwindow), typeof(EngineWindow));
        ((IWindow)window).BeforeClose(); //Outputs: Closing...
        ((IEngineWindow)window).Show();  //Outputs: Showing window...
    }
}

class EngineWindow : IEngineWindow
{
    public void Show()
    {
        Console.WriteLine("Showing window...");
    }
}

//External assembly (dll-file)
class Testwindow : IWindow
{
    public void BeforeClose()
    {
        Console.WriteLine("Closing...");
    }
}

这听起来你需要一个包装器。

  • 让你的内部 class 在其构造函数中获取一个 IWindow 实例
  • 将其存储在私有字段中
  • 实现两个接口
  • 并将所有 IWindow 成员转发到内部实例

更新:如果您考虑 CastleWindsor a simpler approach, here it is (using xUnit 进行测试):

namespace Mixins
{
    using System;
    using Castle.DynamicProxy;
    using Xunit;

    public interface IA
    {
        void Do();
    }

    public interface IB
    {
        void Something();
    }

    public class A : IA
    {
        public void Do()
        {
            throw new NotImplementedException("A");
        }
    }

    public class B : IB
    {
        public void Something()
        {
            throw new NotImplementedException("B");
        }
    }

    public class Blender
    {
        [Fact]
        public void Mix()
        {
            var options = new ProxyGenerationOptions();
            // the instances for A and B would be the user provided and yours
            options.AddMixinInstance(new A());
            options.AddMixinInstance(new B());
            var proxy = new ProxyGenerator().CreateClassProxy<object>(options);

            Assert.IsAssignableFrom<IA>(proxy);
            Assert.IsAssignableFrom<IB>(proxy);

            try
            {
                ((IA)proxy).Do();
            }
            catch (NotImplementedException ex)
            {
                if (ex.Message != "A")
                {
                    throw;
                }
            }

            try
            {
                ((IB)proxy).Something();
            }
            catch (NotImplementedException ex)
            {
                if (ex.Message != "B")
                {
                    throw;
                }
            }

        }
    }
}

我是 NCop 的作者 - 一个可以帮助您实现目标的复合方面框架。
NCop wiki
您基本上需要创建一个新的复合类型接口,它将实现您的两个 window 接口,并使用 TransientComposite 属性将其标记为复合类型。

[TransientComposite]
public interface ICompositeWindow : IWindow, IEngineWindow
{
}

命令 NCop 使用 Mixins 属性在接口和实现之间进行匹配。

[TransientComposite]
[Mixins(typeof(EngineWindow), typeof(Testwindow))]
public interface ICompositeWindow : IWindow, IEngineWindow
{
}

创建一个将发出新类型的 CompositeContainer

class Program
{
    static void Main(string[] args) {
        ICompositeWindow window = null;
        var container = new CompositeContainer();

        container.Configure();
        window = container.Resolve<ICompositeWindow>();
        window.Show();
        window.BeforeClose();
    }
}

你的最终代码应该是:

using System;
using NCop.Composite.Framework;
using NCop.Mixins.Framework;
using NCop.Composite.Runtime;

namespace NCop.Samples
{
    [TransientComposite]
    [Mixins(typeof(EngineWindow), typeof(Testwindow))]
    public interface ICompositeWindow : IWindow, IEngineWindow
    {
    }

    public interface IWindow
    {
        void BeforeClose();
    }

    public interface IEngineWindow
    {
        void Show();
    }

    public class EngineWindow : IEngineWindow
    {
        public void Show() {
            Console.WriteLine("Showing window...");
        }
    }

    public class Testwindow : IWindow
    {
        public void BeforeClose() {
            Console.WriteLine("Closing...");
        }
    }

    class Program
    {
        static void Main(string[] args) {
            ICompositeWindow window = null;
            var container = new CompositeContainer();

            container.Configure();
            window = container.Resolve<ICompositeWindow>();
            window.Show();
            window.BeforeClose();
        }
    }
}