2011-01-03 41 views
6

Đây là bài tập học tập. Tôi đã tạo ra một phương thức lấy một Foo và một chuỗi và đặt thuộc tính A. Tôi đã sử dụng trình gỡ bỏ Reflector để tạo mã phát ra sau. Tháo gỡ trông như thế này:Tạo DynamicMethod để gán giá trị cho một thuộc tính?

.method private hidebysig static void Spork(class ConsoleTesting.Foo f, string 'value') cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: callvirt instance void ConsoleTesting.Foo::set_A(string) 
    L_0007: ret 
} 

Ok, vì vậy tôi theo mô hình đang Emit của tôi sau đó:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 
using System.Reflection.Emit; 


namespace ConsoleTesting 
{ 
    class Foo 
    { 
     public string A { get; set; } 
    } 

    class Program 
    { 
     static Action<Foo, string> GenMethodAssignment(string propName) 
     { 
      MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName); 
      if (setMethod == null) 
       throw new InvalidOperationException("no property setter available"); 

      Type[] argTypes = new Type[] { typeof(Foo), typeof(String) }; 
      DynamicMethod method = new DynamicMethod("__dynamicMethod_Set_" + propName, null, argTypes, typeof(Program)); 
      ILGenerator IL = method.GetILGenerator(); 
      IL.Emit(OpCodes.Ldarg_0); 
      IL.Emit(OpCodes.Ldarg_1); 
      IL.Emit(OpCodes.Callvirt, setMethod); 
      IL.Emit(OpCodes.Ret); 
      method.DefineParameter(1, ParameterAttributes.In, "instance"); 
      method.DefineParameter(2, ParameterAttributes.In, "value"); 

      Action<Foo, string> retval = (Action<Foo, string>)method.CreateDelegate(typeof(Action<Foo, string>)); 
      return retval; 
     } 

     static void Main(string[] args) 
     { 
      Foo f = new Foo(); 
      var meth = GenMethodAssignment("A"); 
      meth(f, "jason"); 
      Console.ReadLine(); 
     } 
    } 

tôi nhận được ngoại lệ này:

JIT Compiler encountered an internal limitation. 

gì cho Miễn phí nào đó có nghĩa là gì và làm thế nào để sửa nó?

EDIT:

Tôi nghĩ có lẽ đó là bởi vì phương pháp mục tiêu là cá nhân, nhưng tôi không như vậy chắc chắn. Từ số DynamicMethod MSDN page:

Ví dụ mã sau tạo ra DynamicMethod được liên kết hợp lý với loại. Hiệp hội này cung cấp cho nó quyền truy cập vào các thành viên riêng tư thuộc loại đó.

+0

Bạn đang truy cập phương thức "get_"; đây có phải là lỗi đánh máy trong bài viết của bạn không? Lưu ý rằng bạn có thể truy cập thuộc tính theo tên và sử dụng phương thức GetSetMethod() trên đối tượng PropertyInfo; theo cách này, bạn không dựa vào quy ước C# của "get_" và "set_". –

+0

Tôi đã làm, tuy nhiên, sửa lỗi chính tả ngay bây giờ. IL chứa AStr, thay vì A, vì tôi đã đổi tên thuộc tính cho mục đích của bài đăng này. – Amy

+0

Ah, tôi đã giải thích sai câu đầu tiên của nhận xét của bạn. Bạn hoàn toàn đúng. – Amy

Trả lời

2

Tôi đã tìm ra. Thay đổi

IL.Emit(OpCodes.Callvirt, setMethod); 

để

IL.Emit(OpCodes.Call, setMethod); 

cố định nó. Tôi không biết tại sao việc tháo gỡ cho thấy CallVirt, nhưng eh.

+0

Phiên bản .NET nào? Trong .NET4 nó đưa ra một ngoại lệ bảo mật để cố gắng gọi một phương thức nằm trên một cá thể. –

1

Phiên bản .NET nào cần thực hiện? Biên dịch một đại biểu từ một cây biểu thức sẽ dễ dàng hơn nhiều.

+0

Trong .Net 3.5 bạn không thể thực hiện phép gán trong cây biểu thức. – Amy

+0

Eh vì tất cả những gì anh ta làm là gọi một phương thức setter bạn có thể làm điều đó trong .NET 3.5. Đơn giản chỉ cần đi 'Expression.Call (target, setMethod, valueExpression)'. Nó sẽ xuất hiện dưới dạng 'target.set_Property (value)' trong chuỗi nhưng nó hoạt động. –

4

Sự cố thú vị. Đầu tiên, bạn có thể thực hiện một vài điều. Nếu bạn chỉ muốn một đại biểu cho setter bạn có thể sử dụng Delegate.CreateDelegate.

Delegate.CreateDelegate(typeof(Action<Foo, string>),typeof(Foo).GetProperty("A").GetSetMethod()) as Action<Foo,String>; 

Nếu bạn đang sử dụng .NET 3.5+ bạn có thể sử dụng cây biểu hiện và tôi khuyên các bạn nên tìm hiểu họ qua DynamicMethod, họ đã hạn chế sử dụng trong .NET 3.5 và hầu như không ai .NET 4. (TypeBuilder vẫn còn rất hữu ích Tuy nhiên).

var targetExpression = Expression.Parameter(typeof(Foo),"target"); 
var valueExpression = Expression.Parameter(typeof(string),"value"); 
var expression = Expression.Lambda<Action<Foo,string>>(
     Expression.Call(
      targetExpression, 
      typeof(Foo).GetProperty("A").GetSetMethod(), 
      valueExpression 
    ), 
     targetExpression, 
     valueExpression 
); 

Trong .NET 4 bạn có thể viết nó đẹp hơn một chút bằng cách sử dụng Expression.Assign, nhưng không tốt hơn nhiều.

Cuối cùng, nếu bạn thực sự muốn làm điều đó trong IL, nó hoạt động.

 DynamicMethod method = new DynamicMethod("Setter", typeof(void), new[] { typeof(Foo), typeof(string) }, true); 
     var ilgen = method.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldarg_0); 
     ilgen.Emit(OpCodes.Ldarg_1); 
     ilgen.Emit(OpCodes.Callvirt, typeof(Foo).GetProperty("A").GetSetMethod()); 
     ilgen.Emit(OpCodes.Ret); 
     var action = method.CreateDelegate(typeof(Action<Foo,string>)) as Action<Foo,string>; 

Tôi nghĩ vấn đề của bạn là bạn đang gọi phương thức thiết lập thực sự là GetMethod lỗi là trên dòng này :: MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName);

Tôi chưa bao giờ được gán thuộc tính tham số nhưng điều đó cũng có thể một vấn đề nữa.

Các vấn đề liên quan