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:
Motivation:
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?
Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.
You can declare variant generic interfaces by using the in and out keywords for generic type parameters. ref , in , and out parameters in C# cannot be variant. Value types also do not support variance. You can declare a generic type parameter covariant by using the out keyword.
Generic Interfaces in Java are the interfaces that deal with abstract data types. Interface help in the independent manipulation of java collections from representation details. They are used to achieving multiple inheritance in java forming hierarchies. They differ from the java class.
The general syntax to declare a generic interface is as follows: interface interface-name<T> { void method-name(T t); // public abstract method. } In the above syntax, <T> is called a generic type parameter that specifies any data type used in the interface.
This isn't pretty, but it seems to work:
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 } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With