2009-07-30 20 views
9

オブジェクトのセットを見ていますが、現在のところ3つのオブジェクトが存在し、共通のインターフェイスを実装しているオブジェクトが4番目同じインターフェースを実装しています。C#で実行時にインターフェイスのクラスを作成する

メソッドとプロパティの第4のオブジェクトの実装では、これら3つの基本オブジェクトの関連ビットを単に呼び出すだけです。ここではそれが意味をなさないケースがあることを知っていますが、これはサービスマルチキャストアーキテクチャのためであり、すでに適切な制限が設定されています。

私の質問はどこから始めるのですか?その4番目のオブジェクトの生成は、実行時にメモリ内で行われる必要がありますので、私はReflection.Emitと思っています。残念ながら、どこで始めるべきかを知るには十分な経験がありません。

インメモリアセンブリを構築する必要がありますか?それは確かにそのように見えるが、私はどこから始めるべきかを簡単に指摘したい。

基本的に私は、そのインターフェイスを実装しているすべてのオブジェクトのインスタンスとそのインターフェイスを実装し、そのインターフェイスを実装する新しいオブジェクトを作成することを検討しています。基礎となるオブジェクトを、できるだけ多くのものに置き換えます。例外などの問題のヒープがありますが、私はそれらに到達するときにそれらのビットに取り組んでいきます。

これはサービス指向アーキテクチャーのためのもので、サービスを使用するコードを変更することなく、複数のロガーサービスにアクセスするためのロガーサービスを例とする既存のコードを使用したいと考えています。代わりに、内部的に複数の基礎となるオブジェクトの関連するメソッドを単に呼び出すロガーサービスラッパーをランタイムで生成したいと思います。

これは.NET 3.5およびC#用です。

+0

私はそれを見つけることができれば、私は実際には数ヶ月前に(ここではSO上)これをしなかった例を書いた...私は参照してくださいよ... –

+2

? http://stackoverflow.com/questions/847809/how-can-i-write-a-generic-container-class-that-implements-a-given-interface-in-c/847975#847975 –

+0

Marc、これを解決するには、質問を重複としてマークするか、または私が受け入れることができる本当の回答を投稿してください。 –

答えて

5

オペレータと、ラムダ/代表者である、あなたのアクション<T>を与えますこれを解決する唯一の方法はReflection.Emitです。 .NET 4.0では

つのメソッド使用するために、コンパイルさExpressionが良いでしょうのでExpressionクラスは、両方のループとステートメント・ブロックをサポートするように拡張されました。しかし、これでもマルチメソッドインターフェイス(単一メソッドデリゲートのみ)はサポートされません。

幸いにも、これまでこれを行っています。 How can I write a generic container class that implements a given interface in C#?

1

実際に実行時にアセンブリを作成する必要がありましたか?

恐らくそれは必要ありません。

C#の瞬間に、はい

を(私は余分なコンテキスト/情報を追加することで、ここで答えを正当化しています)...

+0

IoCシステムのコードです。残念なことに、呼び出すサービスの定義の一部は変更可能な設定ファイルに含まれています。だから、実行時にすることが本当に必要です。 –

+0

実行時にオブジェクトを注入していても、拡張するインタフェースをすでに知っているので、これまでのように、 はコンフィグレーションで呼び出されるサービスを渡しています。 クラスにメソッドを渡して呼び出しオブジェクトに呼び出すことができます... – kentaromiura

5

興味があればここに自分の実装を投稿します。

これは私が受け入れたマークの答えから大きく影響され、コピーされています。

コードは、すべてのオブジェクトが新しいオブジェクトの内部に実装され、そのインタフェースを実装するオブジェクトのセットをラップするために使用できます。返されたオブジェクトのメソッドとプロパティにアクセスすると、同じ方法で、基になるオブジェクトの対応するメソッドとプロパティにアクセスします。

ここにはドラゴンがあります:これは特定の用途のためのものです。これは奇妙な問題の可能性があります。特に、コードでは、すべての基底オブジェクトに、呼び出し先が渡されているオブジェクトとまったく同じオブジェクトが渡されることは保証されていません(または、値を返すメソッドの場合は、最後の戻り値だけが返されます。 out/ref引数に関しては、私はそれがどのように動作するかテストしていませんが、おそらくそうではありません。 あなたは警告されています。そのよう

#region Using 

using System; 
using System.Linq; 
using System.Diagnostics; 
using System.Reflection; 
using System.Reflection.Emit; 
using LVK.Collections; 

#endregion 

namespace LVK.IoC 
{ 
    /// <summary> 
    /// This class implements a service wrapper that can wrap multiple services into a single multicast 
    /// service, that will in turn dispatch all method calls down into all the underlying services. 
    /// </summary> 
    /// <remarks> 
    /// This code is heavily influenced and copied from Marc Gravell's implementation which he 
    /// posted on Stack Overflow here: http://stackoverflow.com/questions/847809 
    /// </remarks> 
    public static class MulticastService 
    { 
     /// <summary> 
     /// Wrap the specified services in a single multicast service object. 
     /// </summary> 
     /// <typeparam name="TService"> 
     /// The type of service to implement a multicast service for. 
     /// </typeparam> 
     /// <param name="services"> 
     /// The underlying service objects to multicast all method calls to. 
     /// </param> 
     /// <returns> 
     /// The multicast service instance. 
     /// </returns> 
     /// <exception cref="ArgumentNullException"> 
     /// <para><paramref name="services"/> is <c>null</c>.</para> 
     /// <para>- or -</para> 
     /// <para><paramref name="services"/> contains a <c>null</c> reference.</para> 
     /// </exception> 
     /// <exception cref="ArgumentException"> 
     /// <para><typeparamref name="TService"/> is not an interface type.</para> 
     /// </exception> 
     public static TService Wrap<TService>(params TService[] services) 
      where TService: class 
     { 
      return (TService)Wrap(typeof(TService), (Object[])services); 
     } 

     /// <summary> 
     /// Wrap the specified services in a single multicast service object. 
     /// </summary> 
     /// <param name="serviceInterfaceType"> 
     /// The <see cref="Type"/> object for the service interface to implement a multicast service for. 
     /// </param> 
     /// <param name="services"> 
     /// The underlying service objects to multicast all method calls to. 
     /// </param> 
     /// <returns> 
     /// The multicast service instance. 
     /// </returns> 
     /// <exception cref="ArgumentNullException"> 
     /// <para><paramref name="serviceInterfaceType"/> is <c>null</c>.</para> 
     /// <para>- or -</para> 
     /// <para><paramref name="services"/> is <c>null</c>.</para> 
     /// <para>- or -</para> 
     /// <para><paramref name="services"/> contains a <c>null</c> reference.</para> 
     /// </exception> 
     /// <exception cref="ArgumentException"> 
     /// <para><typeparamref name="TService"/> is not an interface type.</para> 
     /// </exception> 
     /// <exception cref="InvalidOperationException"> 
     /// <para>One or more of the service objects in <paramref name="services"/> does not implement 
     /// the <paramref name="serviceInterfaceType"/> interface.</para> 
     /// </exception> 
     public static Object Wrap(Type serviceInterfaceType, params Object[] services) 
     { 
      #region Parameter Validation 

      if (Object.ReferenceEquals(null, serviceInterfaceType)) 
       throw new ArgumentNullException("serviceInterfaceType"); 
      if (!serviceInterfaceType.IsInterface) 
       throw new ArgumentException("serviceInterfaceType"); 
      if (Object.ReferenceEquals(null, services) || services.Length == 0) 
       throw new ArgumentNullException("services"); 
      foreach (var service in services) 
      { 
       if (Object.ReferenceEquals(null, service)) 
        throw new ArgumentNullException("services"); 
       if (!serviceInterfaceType.IsAssignableFrom(service.GetType())) 
        throw new InvalidOperationException("One of the specified services does not implement the specified service interface"); 
      } 

      #endregion 

      if (services.Length == 1) 
       return services[0]; 

      AssemblyName assemblyName = new AssemblyName(String.Format("tmp_{0}", serviceInterfaceType.FullName)); 
      String moduleName = String.Format("{0}.dll", assemblyName.Name); 
      String ns = serviceInterfaceType.Namespace; 
      if (!String.IsNullOrEmpty(ns)) 
       ns += "."; 

      var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, 
       AssemblyBuilderAccess.RunAndSave); 
      var module = assembly.DefineDynamicModule(moduleName, false); 
      var type = module.DefineType(String.Format("{0}Multicast_{1}", ns, serviceInterfaceType.Name), 
       TypeAttributes.Class | 
       TypeAttributes.AnsiClass | 
       TypeAttributes.Sealed | 
       TypeAttributes.NotPublic); 
      type.AddInterfaceImplementation(serviceInterfaceType); 

      var ar = Array.CreateInstance(serviceInterfaceType, services.Length); 
      for (Int32 index = 0; index < services.Length; index++) 
       ar.SetValue(services[index], index); 

      // Define _Service0..N-1 private service fields 
      FieldBuilder[] fields = new FieldBuilder[services.Length]; 
      var cab = new CustomAttributeBuilder(
       typeof(DebuggerBrowsableAttribute).GetConstructor(new Type[] { typeof(DebuggerBrowsableState) }), 
       new Object[] { DebuggerBrowsableState.Never }); 
      for (Int32 index = 0; index < services.Length; index++) 
      { 
       fields[index] = type.DefineField(String.Format("_Service{0}", index), 
        serviceInterfaceType, FieldAttributes.Private); 

       // Ensure the field don't show up in the debugger tooltips 
       fields[index].SetCustomAttribute(cab); 
      } 

      // Define a simple constructor that takes all our services as arguments 
      var ctor = type.DefineConstructor(MethodAttributes.Public, 
       CallingConventions.HasThis, 
       Sequences.Repeat(serviceInterfaceType, services.Length).ToArray()); 
      var generator = ctor.GetILGenerator(); 

      // Store each service into its own fields 
      for (Int32 index = 0; index < services.Length; index++) 
      { 
       generator.Emit(OpCodes.Ldarg_0); 
       switch (index) 
       { 
        case 0: 
         generator.Emit(OpCodes.Ldarg_1); 
         break; 

        case 1: 
         generator.Emit(OpCodes.Ldarg_2); 
         break; 

        case 2: 
         generator.Emit(OpCodes.Ldarg_3); 
         break; 

        default: 
         generator.Emit(OpCodes.Ldarg, index + 1); 
         break; 
       } 
       generator.Emit(OpCodes.Stfld, fields[index]); 
      } 
      generator.Emit(OpCodes.Ret); 

      // Implement all the methods of the interface 
      foreach (var method in serviceInterfaceType.GetMethods()) 
      { 
       var args = method.GetParameters(); 
       var methodImpl = type.DefineMethod(method.Name, 
        MethodAttributes.Private | MethodAttributes.Virtual, 
        method.ReturnType, (from arg in args select arg.ParameterType).ToArray()); 
       type.DefineMethodOverride(methodImpl, method); 

       // Generate code to simply call down into each service object 
       // Any return values are discarded, except the last one, which is returned 
       generator = methodImpl.GetILGenerator(); 
       for (Int32 index = 0; index < services.Length; index++) 
       { 
        generator.Emit(OpCodes.Ldarg_0); 
        generator.Emit(OpCodes.Ldfld, fields[index]); 
        for (Int32 paramIndex = 0; paramIndex < args.Length; paramIndex++) 
        { 
         switch (paramIndex) 
         { 
          case 0: 
           generator.Emit(OpCodes.Ldarg_1); 
           break; 

          case 1: 
           generator.Emit(OpCodes.Ldarg_2); 
           break; 

          case 2: 
           generator.Emit(OpCodes.Ldarg_3); 
           break; 

          default: 
           generator.Emit((paramIndex < 255) 
            ? OpCodes.Ldarg_S 
            : OpCodes.Ldarg, 
            paramIndex + 1); 
           break; 
         } 

        } 
        generator.Emit(OpCodes.Callvirt, method); 
        if (method.ReturnType != typeof(void) && index < services.Length - 1) 
         generator.Emit(OpCodes.Pop); // discard N-1 return values 
       } 
       generator.Emit(OpCodes.Ret); 
      } 

      return Activator.CreateInstance(type.CreateType(), services); 
     } 
    } 
} 
+0

この 'Sequences.Repeat'呼び出しは何ですか?そして、 'Sequences'クラスはどこですか?ところで、この素晴らしい答えに感謝! – m1o2

関連する問題