如何在 C# 中编写实现给定接口的通用容器类?
上下文:.NET 3.5、VS2008。 我不确定这个问题的标题,所以也请随意评论这个标题:-)
这是场景:我有几个类,比如 Foo 和 Bar,它们都实现了以下接口:
public interface IStartable
{
void Start();
void Stop();
}
现在我想要一个容器类,它获取 IEnumerable
public class StartableGroup : IStartable // this is the container class
{
private readonly IEnumerable<IStartable> startables;
public StartableGroup(IEnumerable<IStartable> startables)
{
this.startables = startables;
}
public void Start()
{
foreach (var startable in startables)
{
startable.Start();
}
}
public void Stop()
{
foreach (var startable in startables)
{
startable.Stop();
}
}
}
所以我的问题是:如何在不手动编写代码并且不生成代码的情况下做到这一点? 换句话说,我想要像下面这样的东西。
var arr = new IStartable[] { new Foo(), new Bar("wow") };
var mygroup = GroupGenerator<IStartable>.Create(arr);
mygroup.Start(); // --> calls Foo's Start and Bar's Start
约束:
- 没有代码生成(即,在编译时没有真正的文本代码)
- 接口只有 void 方法,带或不带参数
动机:
- 我有一个相当大的应用程序,有很多插件各种接口。 为每个接口手动编写“组容器”类会“重载”项目的类 手动
- 编写代码很容易出错
- 对 IStartable 接口的任何添加或签名更新都将导致“组容器”类中的(手动)更改
- 学习
I明白我必须在这里使用反射,但我宁愿使用一个强大的框架(比如 Castle 的 DynamicProxy< /a> 或 RunSharp) 为我进行接线。
有什么想法吗?
Context: .NET 3.5, VS2008. I'm not sure about the title of this question, so feel free to comment about the title, too :-)
Here's the scenario: I have several classes, say Foo and Bar, all of them implement the following interface:
public interface IStartable
{
void Start();
void Stop();
}
And now I'd like to have a container class, which gets an IEnumerable<IStartable> as an argument in its constructor. This class, in turn, should also implement the IStartable interface:
public class StartableGroup : IStartable // this is the container class
{
private readonly IEnumerable<IStartable> startables;
public StartableGroup(IEnumerable<IStartable> startables)
{
this.startables = startables;
}
public void Start()
{
foreach (var startable in startables)
{
startable.Start();
}
}
public void Stop()
{
foreach (var startable in startables)
{
startable.Stop();
}
}
}
So my question is: how can I do it without manually writing the code, and without code generation? In other words, I'd like to have somethig like the following.
var arr = new IStartable[] { new Foo(), new Bar("wow") };
var mygroup = GroupGenerator<IStartable>.Create(arr);
mygroup.Start(); // --> calls Foo's Start and Bar's Start
Constraints:
- No code generation (that is, no real textual code at compile time)
- The interface has only void methods, with or without arguments
Motivation:
- I have a pretty large application, with a lot of plugins of various interfaces. Manually writing a "group container" class for each interface "overloads" the project with classes
- Manually writing the code is error prone
- Any additions or signature updates to the IStartable interface will lead to (manual) changes in the "group container" class
- Learning
I understand that I have to use reflection here, but I'd rather use a robust framework (like Castle's DynamicProxy or RunSharp) to do the wiring for me.
Any thoughts?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这不太漂亮,但似乎有效:
This isn't pretty, but it seems to work:
它不像基于反射的解决方案那样干净,但一个非常简单且灵活的解决方案是创建一个 ForAll 方法,如下所示:
并且可以像这样调用:
It's not as clean an interface as the reflection based solution, but a very simple and flexible solution is to create a ForAll method like so:
And can be called like so:
您可以子类
List
或其他一些集合类,并使用where
泛型类型约束将T
类型限制为仅IStartable
类。如果您不希望它成为需要类型参数的泛型类,您也可以这样声明该类。
您的示例使用代码将如下所示:
You could subclass
List<T>
or some other collection class and use thewhere
generic type constraint to limit theT
type to be onlyIStartable
classes.You could also declare the class like this if you didn't want it to be a generic class requiring a type parameter.
Your sample usage code would then look something like this:
Automapper
是一个很好的解决方案。 它依赖于下面的LinFu
来创建实例它实现了一个接口,但它负责一些水合作用,并在某种程度上流畅的 api 下混合。LinFu
作者声称它实际上比Castle
的Proxy
更轻量、更快。Automapper
is a good solution to this. It relies onLinFu
underneath to create an instance that implements an interface, but it takes care of some of the hydration, and mixins under a somewhat fluent api. TheLinFu
author claims it is actually much more lightweight and faster thanCastle
'sProxy
.您可以等待 C# 4.0 并使用动态绑定。
这是一个好主意——我曾多次为 IDisposable 实现这个想法; 当我想要处理很多事情时。 但需要记住的一件事是如何处理错误。 如果它记录并继续启动其他人等等...您需要一些选项来上课。
我不熟悉 DynamicProxy 以及如何在这里使用它。
You could wait for C# 4.0 and use dynamic binding.
This is a great idea - I've had to implement this for IDisposable on several occasions; when I want many things to be disposed. One thing to keep in mind though is how errors will be handled. Should it log and keep starting others, etc... You'd need some options to give the class.
I'm not familiar with DynamicProxy and how it could be used here.
您可以使用“List”类及其方法“ForEach”。
You can use the "List" class and their method "ForEach".
如果我理解正确的话,您是在要求实现“GroupGenerator”。
如果没有使用 CastleProxy 的任何实际经验,我的建议是使用 GetMethods() 获取界面中列出的初始方法,然后使用 Reflection.Emit 动态创建一个新类型,其中的新方法枚举对象并调用每个相应的方法方法。 性能应该不会太差。
If I understand correctly, you are asking for an implementation of the "GroupGenerator".
Without any real experience with CastleProxy my recommendation would be to use GetMethods() to get the initial methods listed in the interface and then create a new type on the fly using Reflection.Emit with the new methods that enumerate through the objects and call each corresponding method. The performance shouldn't be too bad.