Используйте Reflection для создания класса (для создания динамического класса FileHelper)

Могу ли я создать класс, как показано ниже, динамически, используя отражение? Методов нет, только общедоступные переменные, некоторые имеют пользовательские атрибуты.

Требуется ли метод .Emit (из того, что я видел, «Emit» выглядит немного сложно).

Я использую программное обеспечение с сайта www.FileHelpers.net, и для него требуется класс. Все мои определения файлов находятся в таблице базы данных, и я хотел бы сделать все более динамичным (т.е. код не меняется, когда в файле появляется новый столбец).

[FileHelpers.DelimitedRecord(",")]
public class FileRow
{
    [FileHelpers.FieldQuoted('"', QuoteMode.OptionalForBoth)] 
    public string Borrower_First_Name;
    [FileHelpers.FieldQuoted('"', QuoteMode.OptionalForBoth)] 
    public string Borrower_Last_Name;
    public string Borrower_Email;
}

Обновление 1. Основываясь на ответе Влада ниже, мне нужно было сослаться на DLL, вот как я это сделал:

    // need to reference the FileHelpers.dll from our own .exe directory 
    string diskFilenameFileHelpersDLL = 
        System.IO.Path.GetDirectoryName(
           System.Reflection.Assembly.GetExecutingAssembly().Location) + 
           @"\FileHelpers.dll";

Обновление 2: Кроме того, после выполнения того, что предложил Влад, я вызываю FileHelper и перебираю результаты. Я, вероятно, перенесу данные в список.

    Assembly assembly = compiledResult.CompiledAssembly;

    // Simple Data Test 
    lineContents = "John,Doe,[email protected]";
    FileHelperEngine engine = new FileHelperEngine(assembly.GetType("FileRow"));
    // FileRow[] FileRowArray = (FileRow[])engine.ReadString(lineContents);
    Object[] FileRowArray = engine.ReadString(lineContents);
    Object myObject = FileRowArray[0];  // only 1 row of data in this example 

    // Get the type handle of a specified class.
    Type myType = assembly.GetType("FileRow");
    // Get the fields of the specified class.
    FieldInfo[] myField = myType.GetFields();

    Console.WriteLine("\nDisplaying fields values:\n");
    for (int i = 0; i < myField.Length; i++)
    {
        Object objTest = myField.GetValue(i);

        string tempName = myField[i].Name;
        Object objTempValue = myField[i].GetValue(myObject);
        string tempValue = System.Convert.ToString(objTempValue);

        Console.WriteLine("The value of {0} is: {1}",
                            tempName, tempValue);

    }

person NealWalters    schedule 23.02.2012    source источник
comment
Взгляните на привет, мир... Отражение. Излучайте стиль!   -  person Devendra D. Chavan    schedule 23.02.2012
comment
Создает ли emit специально .DLL или .EXE? Мне просто нужен класс в памяти, ничего не сохраняется на диск.   -  person NealWalters    schedule 23.02.2012
comment
Я говорил, что мне НЕ нужно сохранять на диск.   -  person NealWalters    schedule 23.02.2012


Ответы (2)


Если ваш код хранится в базе данных в виде строки, вы можете сделать что-то вроде этого, чтобы создать сборку:

Причина, по которой я закомментировал атрибуты, заключается в том, что у меня нет для них пространства имен. Я предполагаю, что у вас есть пространство имен, и вам нужно будет добавить его в свой код для компиляции.

Код работает в LINQPad, поэтому его можно просто скопировать и вставить.

using System;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

void Main()
{
    StringBuilder dc = new StringBuilder(512);
    dc.Append("public class FileRow");
    dc.Append("{");
    //dc.Append("[FileHelpers.FieldQuoted('\"', QuoteMode.OptionalForBoth)]");
    dc.Append("public string Borrower_First_Name;");
    //dc.Append("[FileHelpers.FieldQuoted('\"', QuoteMode.OptionalForBoth)]");
    dc.Append("public string Borrower_Last_Name;");
    dc.Append("public string Borrower_Email;");
    dc.Append("}");

    CompilerResults compiledResult = CompileScript(dc.ToString());

    if (compiledResult.Errors.HasErrors)
    {
        Console.WriteLine (compiledResult.Errors[0].ErrorText);
        throw new InvalidOperationException("Invalid Expression syntax");
    }

    Assembly assembly = compiledResult.CompiledAssembly;

    // This is just for testing purposes.
    FieldInfo field = assembly.GetType("FileRow").GetField("Borrower_First_Name");          
    Console.WriteLine (field.Name);         
    Console.WriteLine (field.FieldType);
}

public static CompilerResults CompileScript(string source) 
{ 
    CompilerParameters parms = new CompilerParameters(); 

    parms.GenerateExecutable = false; 
    parms.GenerateInMemory = true; 
    parms.IncludeDebugInformation = false; 

    CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp"); 

    return compiler.CompileAssemblyFromSource(parms, source); 
} 
person Vlad Bezden    schedule 23.02.2012
comment
Спасибо, попробую в ближайшее время. Мне нужно сделать что-то вроде этого, чтобы использовать его: FileRow fileRow = ParseFileHelperDataRow(rowID, lineContents, sqlConn); Итак, как только я сделаю то, что вы показали выше, я буду использовать отражение для создания своего объекта? - person NealWalters; 23.02.2012
comment
После того, как вы сделаете то, что я показал, теперь у вас будет объект в вашей сборке. Если вы посмотрите на мой пример ниже, он показывает, как вы можете вызывать свойства этого объекта, но вы также можете назначать свойства/поля, вызывать методы для него. Например, если у вас есть метод, вы можете получить этот метод Assembly.GetType(FileRow).GetMethod(MyMethod); а затем вызовите для него вызов. переменная сборки, которую вы получаете, полностью скомпилировала ваш код, и вы можете делать с ней все, что угодно, например обычные объекты/скомпилированный код. - person Vlad Bezden; 23.02.2012
comment
Поэтому мне нужно заполнить свой объект значениями, прежде чем я попытаюсь извлечь эти значения; Я попробую что-то вроде этого: Object myObject = Assembly.GetType(FileRow); myObject = ParseFileHelperDataRow(rowID, lineContents, sqlConn); - person NealWalters; 24.02.2012

Вы также можете использовать CodeDOM для динамического создания своего класса. Для получения дополнительной информации посетите

http://msdn.microsoft.com/en-us/library/ms404245.aspx

person Ramesh    schedule 23.02.2012
comment
Это выглядит легко; Я даже не слышал об этом ... Я полагаю, он тоже может делать атрибуты? - person NealWalters; 23.02.2012
comment
Я вижу, что он также использует Streamwriter для записи на диск. Могу ли я просто создать класс и использовать его в своем коде без записи на диск? - person NealWalters; 23.02.2012
comment
Да, мы можем использовать свойство CustomAttributes. - person Ramesh; 23.02.2012
comment
Да, вы можете просто создать сборку. Вместо provider.GenerateCodeFromCompileUnit используйте метод provider.CompileAssemblyFromDom. - person Ramesh; 23.02.2012
comment
Я думаю, что это приведет меня к тому же концу, что и ответ Влада выше, но, похоже, для меня было меньше кривой обучения, создания кода C # в тексте и его компиляции, а не изучения структуры CodeDOM. - person NealWalters; 24.02.2012