2010-10-05 3 views
153

У меня есть класс, который выглядит следующим образом:Как динамически создавать класс в C#?

public class Field 
{ 
    public string FieldName; 
    public string FieldType; 
} 

и объект List<Field> со значениями:

{"EmployeeID","int"}, 
{"EmployeeName","String"}, 
{"Designation","String"} 

Я хочу создать класс, который выглядит следующим образом:

Class DynamicClass 
{ 
    int EmployeeID, 
    String EmployeeName, 
    String Designation 
} 

Есть какой-либо способ сделать это?

Я хочу, чтобы это генерировалось во время выполнения. Мне не нужен физический CS-файл, который находится в моей файловой системе.

+4

ли Вы хотите использовать этот класс во время выполнения или только генерировать файл? –

+0

Я хочу, чтобы это было создано во время выполнения. Я не хочу, чтобы физический CS-файл находился в моей файловой системе. Извините, что не упоминал об этом раньше. – ashwnacharya

+13

Не могли бы вы дать нам приблизительное представление о том, что вы намерены делать ** с этим классом? – Justin

ответ

207

Да, вы можете использовать для этого пространство имен System.Reflection.Emit. Это не так, если у вас нет опыта, но это, безусловно, возможно.

Редактировать: Этот код может быть ошибочным, но он даст вам общую идею и, надеюсь, отправится на хорошее начало в направлении цели.

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace TypeBuilderNamespace 
{ 
    public static class MyTypeBuilder 
    { 
     public static void CreateNewObject() 
     { 
      var myType = CompileResultType(); 
      var myObject = Activator.CreateInstance(myType); 
     } 
     public static Type CompileResultType() 
     { 
      TypeBuilder tb = GetTypeBuilder(); 
      ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); 

      // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type) 
      foreach (var field in yourListOfFields) 
       CreateProperty(tb, field.FieldName, field.FieldType); 

      Type objectType = tb.CreateType(); 
      return objectType; 
     } 

     private static TypeBuilder GetTypeBuilder() 
     { 
      var typeSignature = "MyDynamicType"; 
      var an = new AssemblyName(typeSignature); 
      AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); 
      ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule"); 
      TypeBuilder tb = moduleBuilder.DefineType(typeSignature, 
        TypeAttributes.Public | 
        TypeAttributes.Class | 
        TypeAttributes.AutoClass | 
        TypeAttributes.AnsiClass | 
        TypeAttributes.BeforeFieldInit | 
        TypeAttributes.AutoLayout, 
        null); 
      return tb; 
     } 

     private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType) 
     { 
      FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); 

      PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); 
      MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); 
      ILGenerator getIl = getPropMthdBldr.GetILGenerator(); 

      getIl.Emit(OpCodes.Ldarg_0); 
      getIl.Emit(OpCodes.Ldfld, fieldBuilder); 
      getIl.Emit(OpCodes.Ret); 

      MethodBuilder setPropMthdBldr = 
       tb.DefineMethod("set_" + propertyName, 
        MethodAttributes.Public | 
        MethodAttributes.SpecialName | 
        MethodAttributes.HideBySig, 
        null, new[] { propertyType }); 

      ILGenerator setIl = setPropMthdBldr.GetILGenerator(); 
      Label modifyProperty = setIl.DefineLabel(); 
      Label exitSet = setIl.DefineLabel(); 

      setIl.MarkLabel(modifyProperty); 
      setIl.Emit(OpCodes.Ldarg_0); 
      setIl.Emit(OpCodes.Ldarg_1); 
      setIl.Emit(OpCodes.Stfld, fieldBuilder); 

      setIl.Emit(OpCodes.Nop); 
      setIl.MarkLabel(exitSet); 
      setIl.Emit(OpCodes.Ret); 

      propertyBuilder.SetGetMethod(getPropMthdBldr); 
      propertyBuilder.SetSetMethod(setPropMthdBldr); 
     } 
    } 
} 
+1

Awesome !! Можете ли вы также рассказать мне, как создать объект типа, возвращаемого методом CompileResultType()? – ashwnacharya

+3

Для этого вы можете использовать System.Activator. Я отвечу на пример. – danijels

+3

Обратите внимание, что вам придется использовать отражение для просмотра, чтения и обновления полей в динамическом типе. Если вы хотите intellisense и никакого отражения, вы должны иметь статический базовый класс или интерфейс, который ваш динамический класс наследует и может быть применен. В этом случае вы можете изменить метод GetTypeBuilder() и изменить вызов moduleBuilder.DefineType, чтобы включить статический тип в качестве последнего параметра (теперь он равен нулю) – danijels

6

Вы хотите посмотреть на CodeDOM. Он позволяет определять элементы кода и компилировать их. Цитирование MSDN:

... Этот объект граф может быть вынесено как исходный код с помощью генератора кода CodeDOM для поддерживаемого программирования языка. Код CodeDOM также можно использовать для компиляции исходного кода в двоичную сборку .

+0

Я хочу, чтобы это было создано во время выполнения , Я не хочу, чтобы физический CS-файл находился в моей файловой системе. Извините, что не упоминал об этом раньше. – ashwnacharya

+0

@ashwnacharya: вы ** можете использовать CodeDOM для генерации исходного файла и компиляции его во время выполнения! – Hemant

+1

Остерегайтесь, однако, что компилятор CodeDOM берет необработанную строку и, следовательно, вы можете захотеть рассмотреть «вставки вставки кода», аналогичные тем, которые используются в XSS и SQL-инъекции. – cwap

2

Вы можете посмотреть на использование динамических модулей и классов, которые могут выполнять эту работу. Единственным недостатком является то, что он остается загруженным в домене приложения. Но с использованием используемой версии .NET framework это может измениться. .NET 4.0 поддерживает коллективные динамические сборки и, следовательно, вы можете динамически воссоздавать классы/типы.

0

Runtime Code Generation with JVM and CLR - Питер Сестофт

Работа для людей, которые действительно заинтересованы в этом типе программирования.

Мой совет для вас состоит в том, что если вы заявляете что-то, попробуйте избежать строки, поэтому, если у вас есть класс Field, лучше использовать класс System.Type, чтобы сохранить тип поля, чем строку. И ради лучших решений вместо создания новых классов попробуйте использовать созданные, но не созданные новые, FiledInfo.

+0

Ссылка мертва: -1 –

55

Это займет определенную работу, но, конечно же, невозможно.

То, что я сделал это:

  • Создать источник C# в строке (нет необходимости писать в файл),
  • Выполнить это через Microsoft.CSharp.CSharpCodeProvider (CompileAssemblyFromSource)
  • Найдите генерироваться Тип
  • И создать экземпляр этого типа (Activator.CreateInstance)

этот вы можете иметь дело с кодом C#, который вы уже знаете, вместо того, чтобы выпустить MSIL.

Но это лучше всего работает, если ваш класс реализует некоторый интерфейс (или получен из некоторого базового слова), иначе как код вызова (read: compiler) должен знать об этом классе, который будет создан во время выполнения?

+2

Любая конкретная причина для downvote? –

+12

выше, потому что вы не заслуживаете нисходящего. – jgauffin

+2

+1: Это может быть хорошим подходом при правильных обстоятельствах. – Ani

13

Я не знаю предполагаемого использования таких динамических классов, а генерация кода и компиляция времени выполнения могут быть выполнены, но при этом требуется некоторое усилие. Может Anonymous Types бы помочь вам, что-то вроде:

var v = new { EmployeeID = 108, EmployeeName = "John Doe" }; 
+1

Вы не можете жестко закодировать имена полей. Он предоставляет им свое поле Field.FieldName. Для жесткого кодирования имена полей побеждают цель. Если вам нужно это сделать, вы можете создать класс. – toddmo

2

Wow! Спасибо за этот ответ! Я добавил некоторые функции, чтобы создать конвертер «datatable to json», который я разделяю с вами.

Public Shared Sub dt2json(ByVal _dt As DataTable, ByVal _sb As StringBuilder) 
    Dim t As System.Type 

    Dim oList(_dt.Rows.Count - 1) As Object 
    Dim jss As New JavaScriptSerializer() 
    Dim i As Integer = 0 

    t = CompileResultType(_dt) 

    For Each dr As DataRow In _dt.Rows 
     Dim o As Object = Activator.CreateInstance(t) 

     For Each col As DataColumn In _dt.Columns 
      setvalue(o, col.ColumnName, dr.Item(col.ColumnName)) 
     Next 

     oList(i) = o 
     i += 1 
    Next 

    jss = New JavaScriptSerializer() 
    jss.Serialize(oList, _sb) 


End Sub 

И в "compileresulttype" к югу, я изменил, что:

For Each column As DataColumn In _dt.Columns 
     CreateProperty(tb, column.ColumnName, column.DataType) 
    Next 


Private Shared Sub setvalue(ByVal _obj As Object, ByVal _propName As String, ByVal _propValue As Object) 
    Dim pi As PropertyInfo 
    pi = _obj.GetType.GetProperty(_propName) 
    If pi IsNot Nothing AndAlso pi.CanWrite Then 
     If _propValue IsNot DBNull.Value Then 
      pi.SetValue(_obj, _propValue, Nothing) 

     Else 
      Select Case pi.PropertyType.ToString 
       Case "System.String" 
        pi.SetValue(_obj, String.Empty, Nothing) 
       Case Else 
        'let the serialiser use javascript "null" value. 
      End Select 

     End If 
    End If 

End Sub 
5

на основе @ danijels отвечают, динамически создать класс в VB.NET:

Imports System.Reflection 
Imports System.Reflection.Emit 

Public Class ObjectBuilder 

Public Property myType As Object 
Public Property myObject As Object 

Public Sub New(fields As List(Of Field)) 
    myType = CompileResultType(fields) 
    myObject = Activator.CreateInstance(myType) 
End Sub 

Public Shared Function CompileResultType(fields As List(Of Field)) As Type 
    Dim tb As TypeBuilder = GetTypeBuilder() 
    Dim constructor As ConstructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName) 

    For Each field In fields 
     CreateProperty(tb, field.Name, field.Type) 
    Next 

    Dim objectType As Type = tb.CreateType() 
    Return objectType 
End Function 

Private Shared Function GetTypeBuilder() As TypeBuilder 
    Dim typeSignature = "MyDynamicType" 
    Dim an = New AssemblyName(typeSignature) 
    Dim assemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run) 
    Dim moduleBuilder As ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule") 
    Dim tb As TypeBuilder = moduleBuilder.DefineType(typeSignature, TypeAttributes.[Public] Or TypeAttributes.[Class] Or TypeAttributes.AutoClass Or TypeAttributes.AnsiClass Or TypeAttributes.BeforeFieldInit Or TypeAttributes.AutoLayout, Nothing) 
    Return tb 
End Function 

Private Shared Sub CreateProperty(tb As TypeBuilder, propertyName As String, propertyType As Type) 
    Dim fieldBuilder As FieldBuilder = tb.DefineField("_" & propertyName, propertyType, FieldAttributes.[Private]) 

    Dim propertyBuilder As PropertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, Nothing) 
    Dim getPropMthdBldr As MethodBuilder = tb.DefineMethod("get_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, propertyType, Type.EmptyTypes) 
    Dim getIl As ILGenerator = getPropMthdBldr.GetILGenerator() 

    getIl.Emit(OpCodes.Ldarg_0) 
    getIl.Emit(OpCodes.Ldfld, fieldBuilder) 
    getIl.Emit(OpCodes.Ret) 

    Dim setPropMthdBldr As MethodBuilder = tb.DefineMethod("set_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, Nothing, {propertyType}) 

    Dim setIl As ILGenerator = setPropMthdBldr.GetILGenerator() 
    Dim modifyProperty As Label = setIl.DefineLabel() 
    Dim exitSet As Label = setIl.DefineLabel() 

    setIl.MarkLabel(modifyProperty) 
    setIl.Emit(OpCodes.Ldarg_0) 
    setIl.Emit(OpCodes.Ldarg_1) 
    setIl.Emit(OpCodes.Stfld, fieldBuilder) 

    setIl.Emit(OpCodes.Nop) 
    setIl.MarkLabel(exitSet) 
    setIl.Emit(OpCodes.Ret) 

    propertyBuilder.SetGetMethod(getPropMthdBldr) 
    propertyBuilder.SetSetMethod(setPropMthdBldr) 
End Sub 

End Class 
0

Вы можете использовать System.Runtime.Remoting.Proxies.RealProxy. Это позволит вам использовать «обычный» код, а не тип сборки с низким уровнем сборки.

Смотрите ответ RealProxy на этот вопрос для хорошего примера:

How do I intercept a method call in C#?

20

Вы можете также динамически создать класс с помощью DynamicObject.

public class DynamicClass : DynamicObject 
{ 
    private Dictionary<string, KeyValuePair<Type, object>> _fields; 

    public DynamicClass(List<Field> fields) 
    { 
     _fields = new Dictionary<string, KeyValuePair<Type, object>>(); 
     fields.ForEach(x => _fields.Add(x.FieldName, 
      new KeyValuePair<Type, object>(x.FieldType, null))); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     if (_fields.ContainsKey(binder.Name)) 
     { 
      var type = _fields[binder.Name].Key; 
      if (value.GetType() == type) 
      { 
       _fields[binder.Name] = new KeyValuePair<Type, object>(type, value); 
       return true; 
      } 
      else throw new Exception("Value " + value + " is not of type " + type.Name); 
     } 
     return false; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     result = _fields[binder.Name].Value; 
     return true; 
    } 
} 

хранить все поля класса в словаре _fields вместе с их типами и значениями. Оба метода позволяют получить или установить значение для некоторых свойств. Для создания экземпляра этого класса необходимо использовать ключевое слово dynamic.

Использование с вашим примером:

var fields = new List<Field>() { 
    new Field("EmployeeID", typeof(int)), 
    new Field("EmployeeName", typeof(string)), 
    new Field("Designation", typeof(string)) 
}; 

dynamic obj = new DynamicClass(fields); 

//set 
obj.EmployeeID = 123456; 
obj.EmployeeName = "John"; 
obj.Designation = "Tech Lead"; 

obj.Age = 25;    //Exception: DynamicClass does not contain a definition for 'Age' 
obj.EmployeeName = 666; //Exception: Value 666 is not of type String 

//get 
Console.WriteLine(obj.EmployeeID);  //123456 
Console.WriteLine(obj.EmployeeName); //John 
Console.WriteLine(obj.Designation); //Tech Lead 

Edit: А вот как выглядит мой класс Field:

public class Field 
{ 
    public Field(string name, Type type) 
    { 
     this.FieldName = name; 
     this.FieldType = type; 
    } 

    public string FieldName; 

    public Type FieldType; 
} 
1

Вы можете также динамически создать класс с помощью DynamicExpressions.

Поскольку «Словарь имеет компактные инициализаторы и управляет столкновениями с ключами, вы захотите сделать что-то подобное.

var list = new Dictionary<string, string> { 
    { 
     "EmployeeID", 
     "int" 
    }, { 
     "EmployeeName", 
     "String" 
    }, { 
     "Birthday", 
     "DateTime" 
    } 
    }; 

Возможно, вы захотите использовать конвертер JSON для создания вашего сериализованного строкового объекта во что-то управляемое.

Затем с использованием System.Linq.Dynamic;

IEnumerable<DynamicProperty> props = list.Select(property => new DynamicProperty(property.Key, Type.GetType(property.Value))).ToList(); 

    Type t = DynamicExpression.CreateClass(props); 

Остальное просто использует System.Reflection.

object obj = Activator.CreateInstance(t); 
    t.GetProperty("EmployeeID").SetValue(obj, 34, null); 
    t.GetProperty("EmployeeName").SetValue(obj, "Albert", null); 
    t.GetProperty("Birthday").SetValue(obj, new DateTime(1976, 3, 14), null); 
} 
1

Я знаю, что снова открываю эту старую задачу, но с C# 6.0 эта задача абсолютно безболезненна.

dynamic expando = new ExpandoObject(); 
expando.EmployeeID=42; 
expando.Designation="unknown"; 
expando.EmployeeName="curt" 

//or more dynamic 
AddProperty(expando, "Language", "English"); 

подробнее см https://www.oreilly.com/learning/building-c-objects-dynamically

Смежные вопросы