2011-01-17 42 views
7

Làm cách nào tôi có thể viết mã C# để biên dịch và chạy mã C# được tạo động. Có những ví dụ xung quanh?Biên dịch mã động bằng C#

Điều tôi đang làm là xây dựng động một lớp C# (hoặc các lớp) và chạy chúng khi chạy. Tôi muốn lớp được tạo ra tương tác với các lớp C# khác không động.

Tôi đã xem các ví dụ tạo tệp exe hoặc dll. Tôi không phải sau đó, tôi chỉ muốn nó biên dịch một số mã C# trong bộ nhớ và sau đó chạy nó. Ví dụ,

Vì vậy, đây là một lớp học mà không phải là năng động, nó sẽ được định nghĩa trong C# lắp ráp của tôi và sẽ chỉ thay đổi tại thời gian biên dịch,

public class NotDynamicClass 
{ 
    private readonly List<string> values = new List<string>(); 

    public void AddValue(string value) 
    { 
     values.Add(value); 
    } 

    public void ProcessValues() 
    { 
     // do some other stuff with values 
    } 
} 

Đây là lớp học của tôi đó là năng động. Mã C# của tôi sẽ tạo ra lớp này và chạy nó,

public class DynamicClass 
{ 
    public static void Main() 
    { 
     NotDynamicClass class = new NotDynamicClass(); 

     class.AddValue("One"); 
     class.AddValue("two"); 
    } 
} 

Vì vậy, kết quả là mã không động của tôi sẽ gọi ProcessValues ​​và nó sẽ làm một số thứ khác. Điểm của mã động là cho phép chúng tôi hoặc khách hàng thêm logic tùy chỉnh vào phần mềm.

Cảm ơn.

+2

Có khả thi để biên dịch các hội đồng "plugin" và chỉ tải các plugin đó vào thời gian chạy thay vì tự động biên dịch các công cụ không? –

+0

Đúng vậy, vâng tôi đã làm điều đó trước đây cho một dự án khác. Điều đó là có thể, nhưng không thực sự là những gì chúng tôi đang có sau cho dự án này. Tôi sẽ xem xét nó, cảm ơn. – peter

Trả lời

15

Có hai khả năng:

  1. Reflection.Emit
  2. System.CodeDom.Compiler

UPDATE:

Như yêu cầu trong phần ý kiến ​​đây là một ví dụ đầy đủ minh họa việc sử dụng Reflection.Emit để tạo động một lớp và thêm một stati c phương pháp cho nó:

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

public class NotDynamicClass 
{ 
    private readonly List<string> values = new List<string>(); 

    public void AddValue(string value) 
    { 
     values.Add(value); 
    } 

    public void ProcessValues() 
    { 
     foreach (var item in values) 
     { 
      Console.WriteLine(item); 
     } 
    } 
} 

class Program 
{ 
    public static void Main() 
    { 
     var assemblyName = new AssemblyName("DynamicAssemblyDemo"); 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
     var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, false); 
     var typeBuilder = moduleBuilder.DefineType("DynamicClass", TypeAttributes.Public); 

     var methodBuilder = typeBuilder.DefineMethod(
      "Main", 
      MethodAttributes.Public | MethodAttributes.Static, 
      null, 
      new Type[0] 
     ); 

     var il = methodBuilder.GetILGenerator(); 
     var ctor = typeof(NotDynamicClass).GetConstructor(new Type[0]); 
     var addValueMi = typeof(NotDynamicClass).GetMethod("AddValue"); 
     il.Emit(OpCodes.Newobj, ctor); 
     il.Emit(OpCodes.Stloc_0); 
     il.DeclareLocal(typeof(NotDynamicClass)); 
     il.Emit(OpCodes.Ldloc_0); 
     il.Emit(OpCodes.Ldstr, "One"); 
     il.Emit(OpCodes.Callvirt, addValueMi); 
     il.Emit(OpCodes.Ldloc_0); 
     il.Emit(OpCodes.Ldstr, "Two"); 
     il.Emit(OpCodes.Callvirt, addValueMi); 
     il.Emit(OpCodes.Ldloc_0); 
     il.Emit(OpCodes.Callvirt, typeof(NotDynamicClass).GetMethod("ProcessValues")); 
     il.Emit(OpCodes.Ret); 
     var t = typeBuilder.CreateType(); 
     var mi = t.GetMethod("Main"); 
     mi.Invoke(null, new object[0]); 
    } 
} 

Bạn có thể đặt điểm ngắt bên trong phương thức không NotDynamicClass và xem cách chúng được gọi.


UPDATE 2:

Dưới đây là một ví dụ với trình biên dịch CodeDOM:

using System; 
using System.CodeDom.Compiler; 
using System.Collections.Generic; 
using Microsoft.CSharp; 

public class NotDynamicClass 
{ 
    private readonly List<string> values = new List<string>(); 

    public void AddValue(string value) 
    { 
     values.Add(value); 
    } 

    public void ProcessValues() 
    { 
     foreach (var item in values) 
     { 
      Console.WriteLine(item); 
     } 
    } 
} 

class Program 
{ 
    public static void Main() 
    { 
     var provider = CSharpCodeProvider.CreateProvider("c#"); 
     var options = new CompilerParameters(); 
     var assemblyContainingNotDynamicClass = Path.GetFileName(Assembly.GetExecutingAssembly().Location); 
     options.ReferencedAssemblies.Add(assemblyContainingNotDynamicClass); 
     var results = provider.CompileAssemblyFromSource(options, new[] 
     { 
@"public class DynamicClass 
{ 
    public static void Main() 
    { 
     NotDynamicClass @class = new NotDynamicClass(); 
     @class.AddValue(""One""); 
     @class.AddValue(""Two""); 
     @class.ProcessValues(); 
    } 
}" 
     }); 
     if (results.Errors.Count > 0) 
     { 
      foreach (var error in results.Errors) 
      { 
       Console.WriteLine(error); 
      } 
     } 
     else 
     { 
      var t = results.CompiledAssembly.GetType("DynamicClass"); 
      t.GetMethod("Main").Invoke(null, null); 
     } 
    } 
} 
+0

Bất kỳ ví dụ hay nào bạn đề nghị sẽ tương ứng với những gì tôi muốn làm ở trên? – peter

+0

@peter, vui lòng xem cập nhật của tôi. Tôi đã thêm một ví dụ minh họa cách bạn có thể xây dựng động một lớp trong thời gian chạy bằng cách thêm các phương thức vào nó và ở cuối gọi các phương thức đó. –

+0

Cảm ơn vì điều đó. Đó là những gì tôi đã sợ và những gì tôi đã thấy ở nơi khác. Bạn đang xây dựng một phương thức bằng cách sử dụng GetILGenerator và gọi il.Emit etc. Không có cách nào để viết mã, chỉ định một chuỗi có mã trong đó và nói 'chạy nó' giống như ví dụ mã thứ hai của tôi ở trên.Lý do là chúng tôi muốn viết các kịch bản này dưới dạng 'văn bản' và lưu trữ chúng trong một DB. – peter

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