การสร้างคลาสแบบไดนามิกจากชนิดที่ถูกดึงข้อมูลขณะรันไทม์


20

เป็นไปได้ไหมที่จะทำสิ่งต่อไปนี้ใน C # (หรือในภาษาอื่น ๆ )?

  1. ฉันกำลังดึงข้อมูลจากฐานข้อมูล ณ รันไทม์ฉันสามารถคำนวณจำนวนคอลัมน์และชนิดข้อมูลของคอลัมน์ที่ดึงมา

  2. ต่อไปฉันต้องการ "สร้าง" คลาสที่มีชนิดข้อมูลเหล่านี้เป็นฟิลด์ ฉันต้องการเก็บบันทึกทั้งหมดที่ฉันได้รับในคอลเลกชัน

ปัญหาคือฉันต้องการทำทั้งขั้นตอนที่1และ2ที่ runtime

เป็นไปได้ไหม ตอนนี้ฉันใช้ C # แต่ฉันสามารถเปลี่ยนไปใช้อย่างอื่นได้ถ้าต้องการ


4
คุณต้องการสิ่งนี้จริงหรือ แน่นอนคุณสามารถ (A) สร้างคลาสที่กำหนดเองตามที่คนอื่นชี้ให้เห็น แต่คุณต้อง (B) รู้วิธีใช้งานในเวลาทำงานด้วย ส่วน (B) ดูเหมือนว่าจะทำงานให้ฉันมากมาย เกิดอะไรขึ้นกับการเก็บข้อมูลไว้ในวัตถุชุดข้อมูลหรือการรวบรวมบางประเภทเช่นพจนานุกรม คุณพยายามจะทำอะไร?
งาน

ทำให้แน่ใจว่าคุณได้ดูงานที่ Rob Conery ทำด้วยdynamicใน Massive: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Robert Harvey

1
Python ช่วยให้การประกาศคลาสแบบไดนามิกและในความเป็นจริงมันเป็นเรื่องธรรมดา มีการสอนโดย David Mertz จากประมาณปี 2001 (ฉันค้นหา แต่ไม่พบลิงก์ที่แน่นอน) มันตรงไปตรงมา
smci

@RobertHarvey ลิงก์ที่คุณแบ่งปันไม่ทำงาน คุณรู้หรือไม่ว่าฉันจะหามันได้จากที่ไหน?
Joze

1
แม้ว่าจะเป็นไปได้ทางเทคนิค แต่ฉันก็ต้องถามถึงคุณค่าของการทำเช่นนั้น จุดสำคัญของการพิมพ์และคลาสที่รัดกุมคือเพื่อให้ในเวลารวบรวมคุณสามารถใช้ข้อมูลคลาสเพื่อตรวจสอบข้อผิดพลาดทางไวยากรณ์ (JITers ไดนามิกรุ่นใหม่สามารถปรับให้เหมาะสมโดยไม่มีสิ่งนี้) เห็นได้ชัดโดยพยายามที่จะใช้พิมพ์แบบไดนามิกในสภาพแวดล้อมการพิมพ์มั่นจะหมายความว่าคุณจะสูญเสียประโยชน์ทั้งหมดของกระบวนทัศน์ทั้ง ...
ศิลปะ

คำตอบ:


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

ใช่คุณสามารถใช้การปล่อยสะท้อนแสงเพื่อทำสิ่งนี้ แมนนิ่งมีสิ่งที่ดูเหมือนจะเป็นหนังสือที่ดีออกมาเกี่ยวกับเรื่องนี้: metaprogramming ใน .NET

BTW: ทำไมไม่ใช้รายการที่มีพจนานุกรมหรือคล้ายกันเพื่อจัดเก็บแต่ละระเบียนด้วยชื่อเขตข้อมูลเป็นกุญแจ?


1
ขอบคุณสำหรับการอ้างอิงการเขียนโปรแกรม Meta นี่เป็นอีกไม่กี่ฉันพบ: vslive.com/Blogs/News-and-Tips/2016/03/…
Leo Gurdian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.