Có thể truyền một biểu thức lambda đến một AppDomain thứ cấp dưới dạng một luồng IL byte và sau đó lắp ráp nó lại bằng DynamicMethod để nó có thể được gọi là?Chuyển một lambda đến một AppDomain thứ cấp như một dòng IL và lắp ráp nó trở lại bằng cách sử dụng DynamicMethod
Tôi không quá chắc chắn đây là cách đúng đắn để đi ở nơi đầu tiên, vì vậy đây là (chi tiết) Lý do tôi hỏi câu hỏi này ...
Trong các ứng dụng của tôi, có rất nhiều trường hợp khi tôi cần phải tải một vài hội đồng để phản ánh, vì vậy tôi có thể xác định những gì cần làm với họ tiếp theo. Phần vấn đề là tôi cần để có thể dỡ bỏ các hội đồng sau khi tôi đã hoàn thành phản ánh trên chúng. Điều này có nghĩa là tôi cần tải chúng bằng cách sử dụng AppDomain
khác.
Hiện tại, hầu hết các trường hợp của tôi đều tương tự nhau, ngoại trừ không hoàn toàn. Ví dụ, đôi khi tôi cần phải trả lại một xác nhận đơn giản, lần khác tôi cần phải tuần tự hóa một luồng tài nguyên từ hội đồng, và một lần nữa, tôi cần phải thực hiện một cuộc gọi lại hoặc hai lần.
Vì vậy, tôi sẽ chỉ viết lại một mã tạo mã tạm thời bán nhiều lần AppDomain
và triển khai các proxy tùy chỉnh MarshalByRefObject
để giao tiếp giữa miền mới và tên miền gốc.
Vì đây là không thực sự có thể chấp nhận được nữa, tôi quyết định viết mã cho tôi một lớp AssemblyReflector
mà có thể được sử dụng theo cách này:
using (var reflector = new AssemblyReflector(@"C:\MyAssembly.dll"))
{
bool isMyAssembly = reflector.Execute(assembly =>
{
return assembly.GetType("MyAssembly.MyType") != null;
});
}
AssemblyReflector
sẽ Automize các AppDomain
dỡ nhờ IDisposable
, và cho phép tôi thực hiện a Func<Assembly,object>
-lambda kiểu giữ mã phản chiếu trong một mã khác AppDomain
một cách minh bạch.
Vấn đề là, lambdas không thể được chuyển sang các miền khác đơn giản như vậy. Vì vậy, sau khi tìm kiếm xung quanh, tôi tìm thấy những gì trông giống như một cách để làm điều đó: chuyển lambda sang AppDomain
mới dưới dạng luồng IL - và điều đó đưa tôi đến câu hỏi ban đầu.
Đây là những gì tôi đã cố gắng, nhưng đã không làm việc (vấn đề đã được BadImageFormatException
bị ném khi cố gắng gọi các đại biểu mới):
public delegate object AssemblyReflectorDelegate(Assembly reflectedAssembly);
public class AssemblyReflector : IDisposable
{
private AppDomain _domain;
private string _assemblyFile;
public AssemblyReflector(string fileName) { ... }
public void Dispose() { ... }
public object Execute(AssemblyReflectorDelegate reflector)
{
var body = reflector.Method.GetMethodBody();
_domain.SetData("IL", body.GetILAsByteArray());
_domain.SetData("MaxStackSize", body.MaxStackSize);
_domain.SetData("FileName", _assemblyFile);
_domain.DoCallBack(() =>
{
var il = (byte[])AppDomain.CurrentDomain.GetData("IL");
var stack = (int)AppDomain.CurrentDomain.GetData("MaxStackSize");
var fileName = (string)AppDomain.CurrentDomain.GetData("FileName");
var args = Assembly.ReflectionOnlyLoadFrom(fileName);
var pars = new Type[] { typeof(Assembly) };
var dm = new DynamicMethod("", typeof(object), pars,
typeof(string).Module);
dm.GetDynamicILInfo().SetCode(il, stack);
var clone = (AssemblyReflectorDelegate)dm.CreateDelegate(
typeof(AssemblyReflectorDelegate));
var result = clone(args); // <-- BadImageFormatException thrown.
AppDomain.CurrentDomain.SetData("Result", result);
});
// Result obviously needs to be serializable for this to work.
return _domain.GetData("Result");
}
}
Tôi thậm chí gần (những gì còn thiếu?), Hay là này một excercise vô nghĩa tất cả trong tất cả?
LƯU Ý: Tôi nhận ra rằng nếu điều này làm việc, tôi vẫn phải cẩn thận về những gì tôi đưa vào lambda liên quan đến tài liệu tham khảo. Đó không phải là một vấn đề, mặc dù.
CẬP NHẬT: Tôi đã cố gắng tiến xa hơn một chút. Có vẻ như chỉ cần gọi SetCode(...)
là không đủ gần để tái tạo lại phương thức. Dưới đây là những gì cần thiết:
// Build a method signature. Since we know which delegate this is, this simply
// means adding its argument types together.
var builder = SignatureHelper.GetLocalVarSigHelper();
builder.AddArgument(typeof(Assembly), false);
var signature = builder.GetSignature();
// This is the tricky part... See explanation below.
di.SetCode(ILTokenResolver.Resolve(il, di, module), stack);
dm.InitLocals = initLocals; // Value gotten from original method's MethodInfo.
di.SetLocalSignature(signature);
Bí quyết như sau. Bản gốc IL chứa các mã thông báo siêu dữ liệu nhất định chỉ hợp lệ trong ngữ cảnh của phương thức gốc. Tôi cần phải phân tích cú pháp IL và thay thế các mã thông báo đó bằng các mã có giá trị trong ngữ cảnh mới. Tôi đã làm điều này bằng cách sử dụng một lớp đặc biệt, ILTokenResolver
, mà tôi đã thích nghi từ hai nguồn sau: Drew Wilson và Haibo Luo.
Vẫn còn một vấn đề nhỏ với điều này - IL mới dường như không chính xác hợp lệ.Tùy thuộc vào nội dung chính xác của lambda, nó có thể hoặc không thể ném một InvalidProgramException khi chạy.
Là một ví dụ đơn giản, hoạt động này:
reflector.Execute(a => { return 5; });
trong khi điều này không:
reflector.Execute(a => { int a = 5; return a; });
Ngoài ra còn có các ví dụ phức tạp hơn được hoặc làm việc hay không, tùy thuộc vào một số yet- sự khác biệt được xác định. Có thể tôi đã bỏ lỡ một số chi tiết nhỏ nhưng quan trọng. Nhưng tôi khá tự tin rằng tôi sẽ tìm thấy nó sau khi so sánh chi tiết hơn về các kết quả đầu ra ildasm. Tôi sẽ đăng những phát hiện của tôi ở đây, khi tôi làm.
EDIT: Oh, man. Tôi hoàn toàn quên câu hỏi này vẫn còn mở. Nhưng vì nó có thể trở nên rõ ràng trong chính nó, tôi đã từ bỏ việc giải quyết vấn đề này. Tôi không hài lòng về nó, đó là chắc chắn. Nó thực sự là một sự xấu hổ, nhưng tôi đoán tôi sẽ chờ đợi hỗ trợ tốt hơn từ khuôn khổ và/hoặc CLR trước khi tôi cố gắng này một lần nữa. Có rất nhiều việc phải làm để thực hiện công việc này, và thậm chí nó không đáng tin cậy. Xin lỗi mọi người quan tâm.
Tôi đã biết điều đó. Nhưng tôi nghĩ rằng sẽ không có vấn đề gì nếu tôi không sử dụng các biến bên ngoài trong lambda? Làm thế nào về nếu tôi sử dụng một đại biểu cũ đồng bằng thay vì lambda? – aoven
Một phần của vấn đề là lambda không chỉ là mã trong biểu thức. Nó cũng biến đổi mã khác trong ứng dụng của bạn tại thời gian biên dịch. Bạn có thể chuyển các thay đổi thời gian biên dịch sang một thay đổi khác, đã được biên dịch, đã được biên dịch. –