Генерация класса динамически из типов, которые выбираются во время выполнения

20

Можно ли сделать следующее в C # (или на любом другом языке)?

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

  2. Далее я хочу «сгенерировать» класс с этими типами данных в качестве полей. Я также хочу хранить все записи, которые я получил в коллекции.

Проблема в том, что я хочу сделать оба шага 1 и 2 во время выполнения

Это возможно? Я использую C # в настоящее время, но я могу перейти к чему-то другому, если мне нужно.

Чани
источник
4
Тебе это действительно нужно? Вы можете (A) сгенерировать собственный класс, как указали другие, но вам также необходимо (B) знать, как использовать его во время выполнения. Часть (B) кажется мне большой работой. Что плохого в хранении данных внутри объекта DataSet или некоторой коллекции, такой как словарь? Что ты пытаешься сделать?
Работа
Убедитесь, что вы ознакомились с работой, которую Роб Конери проделал dynamicв Massive: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Роберт Харви,
1
Python допускает динамическое объявление класса, и на самом деле это распространено. Примерно в 2001 году Дэвидом Мерцем было учебное пособие (я искал его, но не смог найти точную ссылку). Это просто.
smci
@RobertHarvey Ссылка, которой вы поделились, мертва. Вы знаете, где я могу найти это?
Joze
1
Хотя это технически возможно, мне бы хотелось поставить под сомнение ценность этого. Смысл строгой типизации и классов заключается в том, что во время компиляции вы можете использовать информацию о классе для проверки синтаксических ошибок (новое поколение динамических JIT-модулей может оптимизировать без этого). Очевидно, что попытка использовать динамическую типизацию в строго типизированной среде будет означать, что вы потеряете все преимущества обеих парадигм ...
ArTs

Ответы:

28

Используйте CodeDom. Вот с чего начать

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

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            string className = "BlogPost";

            var props = new Dictionary<string, Type>() {
                { "Title", typeof(string) },
                { "Text", typeof(string) },
                { "Tags", typeof(string[]) }
            };

            createType(className, props);
        }

        static void createType(string name, IDictionary<string, Type> props)
        {
            var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
            var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll"}, "Test.Dynamic.dll", false);
            parameters.GenerateExecutable = false;

            var compileUnit = new CodeCompileUnit();
            var ns = new CodeNamespace("Test.Dynamic");
            compileUnit.Namespaces.Add(ns);
            ns.Imports.Add(new CodeNamespaceImport("System"));

            var classType = new CodeTypeDeclaration(name);
            classType.Attributes = MemberAttributes.Public;
            ns.Types.Add(classType);

            foreach (var prop in props)
            {
                var fieldName = "_" + prop.Key;
                var field = new CodeMemberField(prop.Value, fieldName);
                classType.Members.Add(field);

                var property = new CodeMemberProperty();
                property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                property.Type = new CodeTypeReference(prop.Value);
                property.Name = prop.Key;
                property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
                property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
                classType.Members.Add(property);
            }

            var results = csc.CompileAssemblyFromDom(parameters,compileUnit);
            results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
        }
    }
}

Он создает сборку Test.Dynamic.dll с этим классом в нем

namespace Test.Dynamic
{
    public class BlogPost
    {
        private string _Title;
        private string _Text;
        private string[] _Tags;

        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                this._Title = value;
            }
        }
        public string Text
        {
            get
            {
                return this._Text;
            }
            set
            {
                this._Text = value;
            }
        }
        public string[] Tags
        {
            get
            {
                return this._Tags;
            }
            set
            {
                this._Tags = value;
            }
        }
    }
}

Вы также можете использовать динамические функции C #

Класс DynamicEntity, не нужно ничего создавать во время выполнения

public class DynamicEntity : DynamicObject
{
    private IDictionary<string, object> _values;

    public DynamicEntity(IDictionary<string, object> values)
    {
        _values = values;
    }
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _values.Keys;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_values.ContainsKey(binder.Name))
        {
            result = _values[binder.Name];
            return true;
        }
        result = null;
        return false;
    }
}

И используйте это так

var values = new Dictionary<string, object>();
values.Add("Title", "Hello World!");
values.Add("Text", "My first post");
values.Add("Tags", new[] { "hello", "world" });

var post = new DynamicEntity(values);

dynamic dynPost = post;
var text = dynPost.Text;
Майк Кодер
источник
6

Да, вы можете использовать рефлексию для этого. У Мэннинга есть отличная книга, посвященная метапрограммированию в .NET .

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

FinnNk
источник
1
Спасибо за ссылки Мета-программирования. Вот несколько других, которые я нашел: vslive.com/Blogs/News-and-Tips/2016/03/…
Лео Гурдиан,