Динамічне генерування класу з типів, які отримуються під час виконання


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 Посилання, яким ви поділилися, мертве. Ви знаєте, де я можу його знайти?
Жозе

1
Хоча технічно це можливо, мені доведеться поставити під сумнів цінність цього. Суть в сильному введенні та класах полягає в тому, що під час компіляції ви можете використовувати інформацію про клас для перевірки синтаксичних помилок (нове покоління динамічних JITers може оптимізувати без цього). Очевидно, що намагаючись використовувати динамічний набір тексту в сильно набраному середовищі, це означає, що ви втрачаєте всі переваги будь-яких парадигм ...
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 .

BTW: чому б не просто використовувати список, що містить словники чи подібні, для зберігання кожної записи з іменами полів як ключів?


1
Дякую за посилання на метапрограмування. Ось декілька інших, які я знайшов: vslive.com/Blogs/News-and-Tips/2016/03/…
Лев Гурдян
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.