実行時に取得される型からクラスを動的に生成する


20

C#(または他の言語)で次のことを行うことは可能ですか?

  1. データベースからデータを取得しています。実行時に、列の数とフェッチされた列のデータ型を計算できます。

  2. 次に、これらのデータ型をフィールドとしてクラスを「生成」します。また、コレクションにフェッチしたすべてのレコードを保存します。

問題は、実行時にステップ12の両方を実行することです。

これは可能ですか?現在C#を使用していますが、必要に応じて他の何かに移行できます。


4
これは本当に必要ですか?確かに(A)他の人が指摘したようにカスタムクラスを生成できますが、(B)実行時にそれを使用する方法を知る必要もあります。パート(B)は私にとっても多くの仕事のようです。DataSetオブジェクトまたは辞書などの何らかのコレクション内にデータを保持することの何が問題になっていますか?あなたは何をしようとしているのですか?
ジョブ

Rob ConeryがdynamicMassiveで行った作業を必ず確認してください。blog.wekeroad.com / helpy
Robert Harvey

1
Pythonは動的なクラス宣言を許可し、実際には一般的です。2001年頃からDavid Mertzによるチュートリアルがありました(検索しましたが、正確なリンクが見つかりませんでした)。簡単です。
-smci

@RobertHarvey共有したリンクは無効です。どこで見つけられるか知っていますか?
ジョゼ

1
技術的には可能ですが、そうする価値を疑問視する必要があります。強い型付けとクラスのポイントは、コンパイル時にクラス情報を使用して構文エラーをチェックできるようにすることです(新しい世代の動的JITerはこれなしで最適化できます)。明らかに...あなたはどちらかのパラダイムのすべての利点を失うことを意味する強く型付けされた環境での動的型付けを使用しようとすることによって
アーツ

回答:


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でのメタプログラミングに関する優れた本のように見えるものがあります

ところで、辞書を含むリストなどを使用して、フィールド名をキーとして各レコードを保存するのはなぜですか?


1
メタプログラミングのリファレンスをありがとう。ここではいくつかの他の私が見つけたです:vslive.com/Blogs/News-and-Tips/2016/03/...
レオGurdian
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.