动态组合 类 以消除 API 的责任
Dynamically combine classes in order to remove responsibilities from API
我正在构建一个 API(用于游戏引擎),它公开了两个接口,分别称为 IWindow
和 IEngineWindow
。
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();
}
}
}
我正在构建一个 API(用于游戏引擎),它公开了两个接口,分别称为 IWindow
和 IEngineWindow
。
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();
}
}
}