私のコードでは、セッションにサードパーティのスクリプトエンジンが含まれています。エンジンは任意のデリゲートを受け取り、それを同じシグネチャのスクリプトで利用できるようにします。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は、新しいデリゲートのインスタンスを作成することができる方法と、それはデフォルトのセッション値を元のデリゲートを呼び出す作りは私に
私は、完全な長さの例を追加しました。質問を明確にしました。私は閉鎖を発見していない、面白そうに見えるが、それは私にすべての方法を得ることができますか?プラグインが委譲しているもの(第三者コード)がわからないので、 'Func 'のようなものを入力することはできませんので、リフレクションに基づいて署名を生成する必要があります。 –
これはうまくいきますが、ターゲットプラグインライターの聴衆にとってはあまりにも進んでいます:(私たちは、通常のプラグインデリゲートに変換されたスクリプトエンジン用の一般的な 'invoke(string function、string [] params)プラグインデリゲート(セッションなし)と一致するように、呼び出しの周りにラッパー関数を生成するスクリプトをエンジンにプリロードします。 –