gpt4 book ai didi

c# - 在不同的进程中运行委托(delegate)

转载 作者:行者123 更新时间:2023-11-30 16:02:52 29 4
gpt4 key购买 nike

在 C# 中我们有漂亮的 CodeDomProvider类,它允许我们从源动态创建一些 exe 文件,运行它等等。

问题是我们没有这样的来源。例如,我有一个委托(delegate)(例如 Action<string>),我想编译一个 exe,它从命令行获取第一个参数,并通过 Action<string> 执行。用它。在更复杂的情况下,我有 Func<string[], string> , 它接受传递的命令行参数,做一些事情并在标准输出中写一些东西。

我想要类似 string ExecuteOutOfProcess(Func<string[], string> func, string[] args) 的东西,它编译一个 exe,使用提供的参数运行它,从标准输出中获取结果,然后将其作为结果返回。理想情况下,它应该更通用,例如使用 TResult ExecuteOutOfProcess<T,TResult>(Func<T, TResult> func, T input) ,它应该在内部对所有内容进行反序列化和序列化,对调用代码透明。

有什么可以实现的吗?因为做类似事情的唯一方法就是编写一个反编译器,然后从委托(delegate)中获取 C# 源代码,然后将这些源代码与 CodeDomProvider 一起使用,再次解析源代码......没有办法直接在编译器中传递表达式?..

最佳答案

这其实很棘手。但是,如果您确保以某种方式使用它,则可以大大简化它:

  • 只允许调用具有可序列化参数和返回值的静态函数,并且不得触及任何其他托管状态。
  • 由于您需要 64 位 -> 32 位互操作,因此在设置为 AnyCPU 的程序集中声明函数

在这些约束内,您可以使用一个简单的技巧:发送您尝试执行的任何类型和方法名称,您的助手测试运行程序可以使用程序集限定名称加载该类型并调用该方法。例如,要发送所需的数据,您可以使用 WCF 或内存映射文件之类的东西。

一个非常简单(且脆弱)的例子:

public static async Task<T> Run<T>(Func<T> func)
{
var mapName = Guid.NewGuid().ToString();

using (var mapFile = MemoryMappedFile.CreateNew(mapName, 65536))
{
using (var stream = mapFile.CreateViewStream())
using (var bw = new BinaryWriter(stream))
{
bw.Write(func.Method.DeclaringType.AssemblyQualifiedName);
bw.Write(func.Method.Name);

if (func.Target == null)
{
bw.Write(0);
}
else
{
using (var ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, func.Target);
var data = ms.ToArray();
bw.Write(data.Length);
bw.Write(data);
}
}
}

using (var process = Process.Start(new ProcessStartInfo("LambdaRunner", mapName) { UseShellExecute = false, CreateNoWindow = true }))
{
process.EnableRaisingEvents = true;
await process.WaitForExitAsync();

switch (process.ExitCode)
{
case -10: throw new Exception("Type not accessible.");
case -11: throw new Exception("Method not accessible.");
case -12: throw new Exception("Unexpected argument count.");
case -13: throw new Exception("Target missing.");
case 0: break;
}
}

using (var stream = mapFile.CreateViewStream())
{
return (T)(object)new BinaryFormatter().Deserialize(stream);
}
}
}

helper runner 可执行文件如下所示:

static int Main(string[] args)
{
var mapName = args[0];

using (var mapFile = MemoryMappedFile.OpenExisting(mapName))
{
string typeAqn;
string methodName;
byte[] target;

using (var stream = mapFile.CreateViewStream())
using (var br = new BinaryReader(stream))
{
typeAqn = br.ReadString();
methodName = br.ReadString();
target = br.ReadBytes(br.ReadInt32());
}

var type = Type.GetType(typeAqn);
if (type == null) return -10;

var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.InvokeMethod);
if (method == null) return -11;
if (method.GetParameters().Length > 0) return -12;

object returnValue;

if (target.Length == 0)
{
if (!method.IsStatic) return -13;

returnValue = method.Invoke(null, new object[0]);
}
else
{
object targetInstance;
using (var ms = new MemoryStream(target)) targetInstance = new BinaryFormatter().Deserialize(ms);

returnValue = method.Invoke(targetInstance, new object[0]);
}

using (var stream = mapFile.CreateViewStream())
new BinaryFormatter().Serialize(stream, returnValue);

return 0;
}
}

示例用法:

static string HelloWorld1()
{
return "Hello world!";
}

static async Task RunTest<T>(int num, Func<Task<T>> func)
{
try
{
Console.WriteLine($"Test {num}: {await func()}");
}
catch (Exception ex)
{
Console.WriteLine($"Test {num} failed: {ex.Message}");
}
}

[Serializable]
public struct Fun
{
public string Text;
public int Number;

public override string ToString() => $"{Text} ({Number})";
}

static async Task MainAsync(string[] args)
{
await RunTest(1, () => Runner.Run(HelloWorld1));
await RunTest(2, () => Runner.Run(() => "Hello world from a lambda!"));
await RunTest(3, () => Runner.Run(() => 42));
await RunTest(4, () => Runner.Run(() => new Fun{Text = "I also work!", Number = 42}));
}

如果您可以将自己控制在我概述的限制范围内,那么这将非常有效 - 只需确保还添加适当的错误处理。可悲的是,没有简单的方法可以确保您尝试调用的函数是“纯”的——如果某处依赖于某个静态状态,它就不会正常工作(也就是说,它不会使用静态你的过程,而是拥有自己的过程,无论那意味着什么)。

您必须决定这种方法在您的情况下是否值得。它可能使事情变得更简单,也可能使它们变得更糟 :)

关于c# - 在不同的进程中运行委托(delegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37160966/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com