如何编写在C#中实现给定接口的通用容器类?
上下文:.NET 3.5,VS2008。我不确定这个问题的标题,所以也可以随意评论标题:-) 场景如下:我有几个类,比如Foo和Bar,它们都实现了以下接口:如何编写在C#中实现给定接口的通用容器类?,c#,generics,reflection,interface,containers,C#,Generics,Reflection,Interface,Containers,上下文:.NET 3.5,VS2008。我不确定这个问题的标题,所以也可以随意评论标题:-) 场景如下:我有几个类,比如Foo和Bar,它们都实现了以下接口: public interface IStartable { void Start(); void Stop(); } 现在我想要一个容器类,它在构造函数中得到一个IEnumerable作为参数。反过来,此类还应实现IStartable接口: public class StartableGroup : IStartable
public interface IStartable
{
void Start();
void Stop();
}
现在我想要一个容器类,它在构造函数中得到一个IEnumerable作为参数。反过来,此类还应实现IStartable接口:
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();
}
}
}
公共类StartableGroup:IStartable//这是容器类
{
私有只读IEnumerable startables;
公共StartableGroup(IEnumerable startables)
{
this.startables=startables;
}
公开作废开始()
{
foreach(startables中的var startable)
{
startable.Start();
}
}
公共停车场()
{
foreach(startables中的var startable)
{
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
var arr=new-IStartable[]{new-Foo(),new-Bar(“wow”)};
var mygroup=GroupGenerator.Create(arr);
mygroup.Start();//-->调用Foo的Start和Bar的Start
限制条件:
- 没有代码生成(也就是说,编译时没有真正的文本代码)
- 接口只有带或不带参数的void方法
- 我有一个相当大的应用程序,有很多不同接口的插件。为每个接口手动编写一个“组容器”类会“重载”项目中的类
- 手动编写代码容易出错
- 对IStartable接口的任何添加或签名更新都将导致(手动)更改“组容器”类
- 学问
有什么想法吗?您可以将
List
或其他一些集合类子类化,并使用where
泛型类型约束将T
类型限制为仅IStartable
类
class StartableList<T> : List<T>, IStartable where T : IStartable
{
public StartableList(IEnumerable<T> arr)
: base(arr)
{
}
public void Start()
{
foreach (IStartable s in this)
{
s.Start();
}
}
public void Stop()
{
foreach (IStartable s in this)
{
s.Stop();
}
}
}
类StartableList:List,IStartable其中T:IStartable
{
公共起始表列表(IEnumerable arr)
:基准(arr)
{
}
公开作废开始()
{
foreach(此中的IStartable s)
{
s、 Start();
}
}
公共停车场()
{
foreach(此中的IStartable s)
{
s、 停止();
}
}
}
如果不希望类是需要类型参数的泛型类,也可以这样声明该类
public class StartableList : List<IStartable>, IStartable
{ ... }
公共类StartableList:List,可启动
{ ... }
您的示例使用代码如下所示:
var arr = new IStartable[] { new Foo(), new Bar("wow") };
var mygroup = new StartableList<IStartable>(arr);
mygroup.Start(); // --> calls Foo's Start and Bar's Start
var arr=new-IStartable[]{new-Foo(),new-Bar(“wow”)};
var mygroup=新的可启动列表(arr);
mygroup.Start();//-->调用Foo的Start和Bar的Start
您可以等待C#4.0并使用动态绑定
这是一个好主意——我已经在好几个场合为IDisposable实现了它;当我想处理很多事情的时候。但是要记住的一件事是如何处理错误。是否应该记录并继续启动其他程序等。。。你需要一些选项来给这个班上课
我不熟悉DynamicProxy及其在这里的使用方法。您可以使用“List”类及其方法“ForEach”
var startables=新列表(一个或多个startables的数组);
startables.ForEach(t=>t.Start();}
如果我理解正确,您要求实现“GroupGenerator”
如果没有CastleProxy的任何实际经验,我的建议是使用GetMethods()获取界面中列出的初始方法,然后使用反射动态创建一个新类型。使用枚举对象的新方法发出,并调用每个相应的方法。性能应该不会太差。这并不漂亮,但似乎可以:
public static class GroupGenerator
{
public static T Create<T>(IEnumerable<T> items) where T : class
{
return (T)Activator.CreateInstance(Cache<T>.Type, items);
}
private static class Cache<T> where T : class
{
internal static readonly Type Type;
static Cache()
{
if (!typeof(T).IsInterface)
{
throw new InvalidOperationException(typeof(T).Name
+ " is not an interface");
}
AssemblyName an = new AssemblyName("tmp_" + typeof(T).Name);
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(
an, AssemblyBuilderAccess.RunAndSave);
string moduleName = Path.ChangeExtension(an.Name,"dll");
var module = asm.DefineDynamicModule(moduleName, false);
string ns = typeof(T).Namespace;
if (!string.IsNullOrEmpty(ns)) ns += ".";
var type = module.DefineType(ns + "grp_" + typeof(T).Name,
TypeAttributes.Class | TypeAttributes.AnsiClass |
TypeAttributes.Sealed | TypeAttributes.NotPublic);
type.AddInterfaceImplementation(typeof(T));
var fld = type.DefineField("items", typeof(IEnumerable<T>),
FieldAttributes.Private);
var ctor = type.DefineConstructor(MethodAttributes.Public,
CallingConventions.HasThis, new Type[] { fld.FieldType });
var il = ctor.GetILGenerator();
// store the items
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fld);
il.Emit(OpCodes.Ret);
foreach (var method in typeof(T).GetMethods())
{
var args = method.GetParameters();
var methodImpl = type.DefineMethod(method.Name,
MethodAttributes.Private | MethodAttributes.Virtual,
method.ReturnType,
Array.ConvertAll(args, arg => arg.ParameterType));
type.DefineMethodOverride(methodImpl, method);
il = methodImpl.GetILGenerator();
if (method.ReturnType != typeof(void))
{
il.Emit(OpCodes.Ldstr,
"Methods with return values are not supported");
il.Emit(OpCodes.Newobj, typeof(NotSupportedException)
.GetConstructor(new Type[] {typeof(string)}));
il.Emit(OpCodes.Throw);
continue;
}
// get the iterator
var iter = il.DeclareLocal(typeof(IEnumerator<T>));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fld);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerable<T>)
.GetMethod("GetEnumerator"), null);
il.Emit(OpCodes.Stloc, iter);
Label tryFinally = il.BeginExceptionBlock();
// jump to "progress the iterator"
Label loop = il.DefineLabel();
il.Emit(OpCodes.Br_S, loop);
// process each item (invoke the paired method)
Label doItem = il.DefineLabel();
il.MarkLabel(doItem);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator<T>)
.GetProperty("Current").GetGetMethod(), null);
for (int i = 0; i < args.Length; i++)
{ // load the arguments
switch (i)
{
case 0: il.Emit(OpCodes.Ldarg_1); break;
case 1: il.Emit(OpCodes.Ldarg_2); break;
case 2: il.Emit(OpCodes.Ldarg_3); break;
default:
il.Emit(i < 255 ? OpCodes.Ldarg_S
: OpCodes.Ldarg, i + 1);
break;
}
}
il.EmitCall(OpCodes.Callvirt, method, null);
// progress the iterator
il.MarkLabel(loop);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator)
.GetMethod("MoveNext"), null);
il.Emit(OpCodes.Brtrue_S, doItem);
il.Emit(OpCodes.Leave_S, tryFinally);
// dispose iterator
il.BeginFinallyBlock();
Label endFinally = il.DefineLabel();
il.Emit(OpCodes.Ldloc, iter);
il.Emit(OpCodes.Brfalse_S, endFinally);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IDisposable)
.GetMethod("Dispose"), null);
il.MarkLabel(endFinally);
il.EndExceptionBlock();
il.Emit(OpCodes.Ret);
}
Cache<T>.Type = type.CreateType();
#if DEBUG // for inspection purposes...
asm.Save(moduleName);
#endif
}
}
}
公共静态类GroupGenerator
{
公共静态T创建(IEnumerable项),其中T:class
{
return(T)Activator.CreateInstance(Cache.Type,items);
}
私有静态类缓存,其中T:class
{
内部静态只读类型;
静态缓存()
{
if(!typeof(T).i接口)
{
抛出新的InvalidOperationException(typeof(T).Name
+“不是接口”);
}
AssemblyName an=新的AssemblyName(“tmp_”+typeof(T).Name);
var asm=AppDomain.CurrentDomain.DefinedDynamicAssembly(
an,AssemblyBuilderAccess.RunAndSave);
字符串moduleName=Path.ChangeExtension(一个.Name,“dll”);
var module=asm.defineddynamicmodule(moduleName,false);
字符串ns=typeof(T).Namespace;
如果(!string.IsNullOrEmpty(ns))ns+=”;
变量类型=module.DefineType(ns+“grp_u2;”+typeof(T).Name,
TypeAttributes.Class | TypeAttributes.AnsiClass|
TypeAttributes.Sealed | TypeAttributes.NotPublic);
类型。AddInterfaceImplementation(类型(T));
var fld=类型。定义字段(“项目”,类型(IEnumerable),
字段属性(私有);
var ctor=type.DefineConstructor(MethodAttributes.Public,
CallingConventions.HasThis,新类型[]{fld.FieldType});
var il=ctor.GetILGenerator();
//储存物品
il.Emit(操作码.Ldarg_0);
il.Emit(操作码Ldarg_1);
il.Emit(操作码.Stfld,fld);
发射(操作码Ret);
foreach(typeof(T).GetMethods()中的var方法)
{
var args=met
public static class GroupGenerator
{
public static T Create<T>(IEnumerable<T> items) where T : class
{
return (T)Activator.CreateInstance(Cache<T>.Type, items);
}
private static class Cache<T> where T : class
{
internal static readonly Type Type;
static Cache()
{
if (!typeof(T).IsInterface)
{
throw new InvalidOperationException(typeof(T).Name
+ " is not an interface");
}
AssemblyName an = new AssemblyName("tmp_" + typeof(T).Name);
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(
an, AssemblyBuilderAccess.RunAndSave);
string moduleName = Path.ChangeExtension(an.Name,"dll");
var module = asm.DefineDynamicModule(moduleName, false);
string ns = typeof(T).Namespace;
if (!string.IsNullOrEmpty(ns)) ns += ".";
var type = module.DefineType(ns + "grp_" + typeof(T).Name,
TypeAttributes.Class | TypeAttributes.AnsiClass |
TypeAttributes.Sealed | TypeAttributes.NotPublic);
type.AddInterfaceImplementation(typeof(T));
var fld = type.DefineField("items", typeof(IEnumerable<T>),
FieldAttributes.Private);
var ctor = type.DefineConstructor(MethodAttributes.Public,
CallingConventions.HasThis, new Type[] { fld.FieldType });
var il = ctor.GetILGenerator();
// store the items
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fld);
il.Emit(OpCodes.Ret);
foreach (var method in typeof(T).GetMethods())
{
var args = method.GetParameters();
var methodImpl = type.DefineMethod(method.Name,
MethodAttributes.Private | MethodAttributes.Virtual,
method.ReturnType,
Array.ConvertAll(args, arg => arg.ParameterType));
type.DefineMethodOverride(methodImpl, method);
il = methodImpl.GetILGenerator();
if (method.ReturnType != typeof(void))
{
il.Emit(OpCodes.Ldstr,
"Methods with return values are not supported");
il.Emit(OpCodes.Newobj, typeof(NotSupportedException)
.GetConstructor(new Type[] {typeof(string)}));
il.Emit(OpCodes.Throw);
continue;
}
// get the iterator
var iter = il.DeclareLocal(typeof(IEnumerator<T>));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fld);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerable<T>)
.GetMethod("GetEnumerator"), null);
il.Emit(OpCodes.Stloc, iter);
Label tryFinally = il.BeginExceptionBlock();
// jump to "progress the iterator"
Label loop = il.DefineLabel();
il.Emit(OpCodes.Br_S, loop);
// process each item (invoke the paired method)
Label doItem = il.DefineLabel();
il.MarkLabel(doItem);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator<T>)
.GetProperty("Current").GetGetMethod(), null);
for (int i = 0; i < args.Length; i++)
{ // load the arguments
switch (i)
{
case 0: il.Emit(OpCodes.Ldarg_1); break;
case 1: il.Emit(OpCodes.Ldarg_2); break;
case 2: il.Emit(OpCodes.Ldarg_3); break;
default:
il.Emit(i < 255 ? OpCodes.Ldarg_S
: OpCodes.Ldarg, i + 1);
break;
}
}
il.EmitCall(OpCodes.Callvirt, method, null);
// progress the iterator
il.MarkLabel(loop);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator)
.GetMethod("MoveNext"), null);
il.Emit(OpCodes.Brtrue_S, doItem);
il.Emit(OpCodes.Leave_S, tryFinally);
// dispose iterator
il.BeginFinallyBlock();
Label endFinally = il.DefineLabel();
il.Emit(OpCodes.Ldloc, iter);
il.Emit(OpCodes.Brfalse_S, endFinally);
il.Emit(OpCodes.Ldloc, iter);
il.EmitCall(OpCodes.Callvirt, typeof(IDisposable)
.GetMethod("Dispose"), null);
il.MarkLabel(endFinally);
il.EndExceptionBlock();
il.Emit(OpCodes.Ret);
}
Cache<T>.Type = type.CreateType();
#if DEBUG // for inspection purposes...
asm.Save(moduleName);
#endif
}
}
}
static void ForAll<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (T item in items)
{
action(item);
}
}
arr.ForAll(x => x.Start());