Membuat kelas secara dinamis dari tipe yang diambil saat runtime

20

Apakah mungkin melakukan hal berikut dalam C # (atau dalam bahasa lain)?

  1. Saya mengambil data dari database. Pada saat dijalankan saya dapat menghitung jumlah kolom dan tipe data dari kolom yang diambil.

  2. Selanjutnya saya ingin "menghasilkan" kelas dengan tipe data ini sebagai bidang. Saya juga ingin menyimpan semua catatan yang saya ambil dalam koleksi.

Masalahnya adalah saya ingin melakukan langkah 1 dan 2 saat runtime

Apakah ini mungkin? Saya menggunakan C # saat ini tetapi saya dapat beralih ke sesuatu yang lain jika perlu.

Chani
sumber
4
Apakah Anda benar-benar membutuhkan ini? Anda pasti dapat (A) menghasilkan kelas khusus seperti yang ditunjukkan orang lain, tetapi Anda juga perlu (B) tahu cara menggunakannya pada waktu berjalan. Bagian (B) sepertinya juga banyak pekerjaan bagiku. Apa yang salah dengan menyimpan data di dalam objek DataSet atau semacam koleksi seperti kamus? Apa yang sedang Anda coba lakukan?
Pekerjaan
Pastikan Anda melihat pekerjaan yang dilakukan Rob Conery dynamicdi Massive: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Robert Harvey
1
Python memungkinkan untuk deklarasi kelas dinamis, dan pada kenyataannya itu umum. Ada tutorial oleh David Mertz dari sekitar tahun 2001 (saya mencarinya tetapi tidak dapat menemukan tautan yang tepat). Sangat mudah.
smci
@RobertHarvey Tautan yang Anda bagikan sudah mati. Apakah Anda tahu di mana saya dapat menemukannya?
Joze
1
Meskipun secara teknis memungkinkan, saya harus mempertanyakan nilai dari melakukannya. Inti dari pengetikan dan kelas yang kuat adalah agar pada waktu kompilasi Anda dapat menggunakan informasi kelas untuk memeriksa kesalahan sintaksis (generasi JITers dinamis yang lebih baru dapat mengoptimalkan tanpa ini). Jelas dengan mencoba menggunakan pengetikan dinamis di lingkungan yang sangat diketik akan berarti Anda kehilangan semua keuntungan dari kedua paradigma ...
ArTs

Jawaban:

28

Gunakan CodeDom. Inilah sesuatu untuk memulai

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));
        }
    }
}

Itu menciptakan sebuah perakitan 'Test.Dynamic.dll' dengan kelas ini di dalamnya

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;
            }
        }
    }
}

Anda juga dapat menggunakan fitur dinamis C #

Kelas DynamicEntity, tidak perlu membuat apa pun saat runtime

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;
    }
}

Dan gunakan seperti ini

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;
Mike Koder
sumber
6

Ya, Anda dapat menggunakan refleksi memancarkan untuk melakukan ini. Manning memiliki apa yang tampaknya menjadi buku yang sangat bagus tentang ini: Metaprogramming dalam .NET .

BTW: mengapa tidak hanya menggunakan daftar yang berisi kamus atau serupa untuk menyimpan setiap catatan dengan nama bidang sebagai kunci?

FinnNk
sumber
1
Terima kasih atas referensi Meta-programming. Berikut beberapa lainnya yang saya temukan: vslive.com/Blogs/News-and-Tips/2016/03/...
Leo Gurdian