2017-08-25 15 views
0

私のコードでは、セッションにサードパーティのスクリプトエンジンが含まれています。エンジンは任意のデリゲートを受け取り、それを同じシグネチャのスクリプトで利用できるようにします。C#:動的に部分的な関数を作成する

これでエンジン用のデリゲートを提供するプラグインが必要になりましたが、セッション内の余分なデータをスクリプト内に表示したくない場合もあります。

デリゲートを消費するスクリプトは、セッションについてはわかりませんが、それを実装するプラグインはありません。プラグインライターは、プラグインデリゲートに対して任意の数または種類の引数を自由に使用する必要があるため、実行時に動的に行う必要があります。例えば

:スクリプトエンジンがLogMessage("Test")を呼び出したときに

//from available plugin delegates 
delegate bool SendMessage(Session info, string ip, int port, string message); 
delegate void LogMessage(Session info, string message); 

//to create script delegates 
delegate bool SendMessage(string ip, int port, string message); 
delegate void LogMessage(string message); 

だから、プラグインでLogMessage(mysession, "Test")を呼び出す必要があります。

デリゲートにデフォルトを追加するためのcurryに関する情報が見つかりました。Reflectionはデリゲートを作成することができましたが、これをどのように行うことができますか?

編集:私は今、元

/// <summary> 
/// Based on code from user svick [https://stackoverflow.com/questions/9505117/creating-delegates-dynamically-with-parameter-names] 
/// </summary> 
class DelegateTypeFactory 
{ 
    private readonly ModuleBuilder _module; 

    public DelegateTypeFactory() 
    { 
     //Build in-memory assembly to contain the new types 
     AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DelegateTypeFactory"), AssemblyBuilderAccess.RunAndCollect); 
     _module = assembly.DefineDynamicModule("DelegateTypeFactory"); 
    } 

    public Type CreateDelegateType(MethodInfo method) 
    { 
     //Create new name for the type to avoid clashes 
     string nameBase = string.Format("{0}{1}", method.DeclaringType.Name, method.Name); 
     string name = GetUniqueName(nameBase); 

     //Create the toolset to make the new type 
     TypeBuilder builder = _module.DefineType(name, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate)); 
     ConstructorBuilder constructor = builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) }); 
     constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); 

     //define the methods params and filter unwanted param 
     ParameterInfo[] parameters = method.GetParameters(); 
     parameters = parameters.Where(p => p.ParameterType != typeof(Session)).ToArray(); 

     //design the method signature 
     MethodBuilder invokeMethod = builder.DefineMethod("Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray()); 
     invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask); 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name); 
     } 

     //Return the newly created delegate type 
     return builder.CreateType(); 
    } 

    private string GetUniqueName(string nameBase) 
    { 
     int number = 2; 
     string name = nameBase; 
     while (_module.GetType(name) != null) 
     { 
      name = $"{nameBase}{number++}"; 
     } 
     return name; 
    } 
} 

使用その後、以下の変数を持つデリゲート型を作成することができ

進捗更新:2全長例

public class Session 
{ 
    //Some metadata here 
} 

public class Plugin 
{ 
    private delegate bool SendMessage(Session info, string ip, int port, string message); 
    private delegate void LogMessage(Session info, string message); 

    public Delegate[] GetFunctions() 
    { 
     return new Delegate[] { new SendMessage(HandleSendMessage), new LogMessage(HandleLogMessage) }; 
    } 

    private bool HandleSendMessage(Session info, string ip, int port, string message) 
    { 
     Console.WriteLine($"SEND {ip}:{port} >> \"{message}\""); 
     return true; 
    } 

    private void HandleLogMessage(Session info, string message) 
    { 
     Console.WriteLine($"LOG \"{message}\""); 
    } 
} 

//stand-in for 3rd party code 
public class Engine 
{ 
    private IEnumerable<Delegate> _functions = null; 

    public void Add(IEnumerable<Delegate> functions) 
    { 
     //ignore this code, just simulating 3rd party behavior 
     _functions = functions; 
    } 

    public void Execute() 
    { 
     //ignore this code, just simulating 3rd party behavior 
     foreach (Delegate function in _functions) 
     { 
      ParameterInfo[] fparams = function.Method.GetParameters(); 
      int n = fparams.Count(); 
      object[] args = new object[n]; 
      for (int i = 0; i < n; i++) 
      { 
       if (string.Compare(fparams[i].Name, "ip") == 0) 
       { 
        args[i] = "127.0.0.1"; 
       } 
       else if (string.Compare(fparams[i].Name, "port") == 0) 
       { 
        args[i] = 80; 
       } 
       else if (string.Compare(fparams[i].Name, "message") == 0) 
       { 
        args[i] = "Some message"; 
       } 
       else if (string.Compare(fparams[i].Name, "info") == 0) 
       { 
        Console.WriteLine("Error this should not be here"); 
        args[i] = null; 
       } 
      } 
      function.DynamicInvoke(args); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..) 
     Engine e = new Engine(); //stand-in for 3rd party code 
     List<Delegate> newDelegates = new List<Delegate>(); 

     foreach (Delegate d in p.GetFunctions()) 
     { 
      //QUESTION: create a new delegate same as (d) minus the first param (Session info) 
      //QUESTION: link the new delegate to (d) and set (Session info) to some value 

      newDelegates.Add(d); //add new delegate instead of (d) 
     } 

     e.Add(newDelegates); 
     e.Execute(); 
    } 
} 

EDIT :

DelegateTypeFactory factory = new ConsoleApplication1.DelegateTypeFactory(); 
Type newDelegateType = factory .CreateDelegateType(originalDelegate.Method); 

しかし1は、新しいデリゲートのインスタンスを作成することができる方法と、それはデフォルトのセッション値を元のデリゲートを呼び出す作りは私に

答えて

1

を見逃さあなたがエンジンにデリゲートを渡すプラグインを持っているように思えます。

エンジンは、プラグインを動的に起動します。

これはクロージャで行うことができますが、プラグインはデリゲートを作成しているのでクロージャを作成する必要があります。 サードパーティの開発者もこの手法を使うことができるので、それは彼らの責任です。デリゲートで使用できる余分なオブジェクトが必要ない場合、それらは必要ありません。

エンジンには、デリゲートが他の変数をキャプチャしていることがわかります。

mainには、そこにプラグイン機能を変更しようとしていることを示すコメントがあります。 プラグインの作者がどのようなパラメタを表示したいのかわからないので、そこでどのように行うのか分かりません。

私はこれを書いて、プラグインが隠したいものを決定できるようにしました。

私はHandle *メソッドを書いた方法で去りましたが、必要に応じてセッションオブジェクトにアクセスできました。

public class Session 
{ 
    //Some metadata here 
} 

public class Plugin 
{ 
    private delegate bool SendMessage(string ip, int port, string message); 
    private delegate void LogMessage(string message); 

    public Delegate[] GetFunctions() 
    { 
     var sessionInfo = new Session(); 
     return new Delegate[] { new SendMessage(HandleSendMessage(sessionInfo)), new LogMessage(HandleLogMessage(sessionInfo)) }; 
    } 

    private SendMessage HandleSendMessage(Session info) 
    { 
     return delegate (string ip, int port, string message) 
     { 
      Console.WriteLine($"SEND {ip}:{port} >> \"{message}\""); 
      return true; 
     }; 
    } 

    private LogMessage HandleLogMessage(Session info) 
    { 
     return delegate (string message) 
     { 
      Console.WriteLine($"LOG \"{message}\""); 
     }; 
    } 
} 

//stand-in for 3rd party code 
public class Engine 
{ 
    private IEnumerable<Delegate> _functions = null; 

    public void Add(IEnumerable<Delegate> functions) 
    { 
     //ignore this code, just simulating 3rd party behavior 
     _functions = functions; 
    } 

    public void Execute() 
    { 
     //ignore this code, just simulating 3rd party behavior 
     foreach (Delegate function in _functions) 
     { 
      ParameterInfo[] fparams = function.Method.GetParameters(); 
      int n = fparams.Count(); 
      object[] args = new object[n]; 
      for (int i = 0; i < n; i++) 
      { 
       if (string.Compare(fparams[i].Name, "ip") == 0) 
       { 
        args[i] = "127.0.0.1"; 
       } 
       else if (string.Compare(fparams[i].Name, "port") == 0) 
       { 
        args[i] = 80; 
       } 
       else if (string.Compare(fparams[i].Name, "message") == 0) 
       { 
        args[i] = "Some message"; 
       } 
       else if (string.Compare(fparams[i].Name, "info") == 0) 
       { 
        Console.WriteLine("Error this should not be here"); 
        args[i] = null; 
       } 
      } 
      function.DynamicInvoke(args); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..) 
     Engine e = new Engine(); //stand-in for 3rd party code 
     List<Delegate> newDelegates = new List<Delegate>(); 

     foreach (Delegate d in p.GetFunctions()) 
     { 
      //QUESTION: create a new delegate same as (d) minus the first param (Session info) 
      //QUESTION: link the new delegate to (d) and set (Session info) to some value 

      newDelegates.Add(d); //add new delegate instead of (d) 
     } 

     e.Add(newDelegates); 
     e.Execute(); 

    } 
} 
+0

私は、完全な長さの例を追加しました。質問を明確にしました。私は閉鎖を発見していない、面白そうに見えるが、それは私にすべての方法を得ることができますか?プラグインが委譲しているもの(第三者コード)がわからないので、 'Func 'のようなものを入力することはできませんので、リフレクションに基づいて署名を生成する必要があります。 –

+0

これはうまくいきますが、ターゲットプラグインライターの聴衆にとってはあまりにも進んでいます:(私たちは、通常のプラグインデリゲートに変換されたスクリプトエンジン用の一般的な 'invoke(string function、string [] params)プラグインデリゲート(セッションなし)と一致するように、呼び出しの周りにラッパー関数を生成するスクリプトをエンジンにプリロードします。 –