런타임에 가져온 유형에서 클래스를 동적으로 생성


20

C # (또는 다른 언어)에서 다음을 수행 할 수 있습니까?

  1. 데이터베이스에서 데이터를 가져오고 있습니다. 런타임에 가져온 열의 열 수와 데이터 유형을 계산할 수 있습니다.

  2. 다음으로 이러한 데이터 형식을 필드로 사용하여 클래스를 "생성"하려고합니다. 또한 컬렉션에서 가져온 모든 레코드를 저장하고 싶습니다.

문제는 런타임에 1 단계 와 2 단계를 모두 수행하고 싶다는 것입니다.

이것이 가능한가? 현재 C #을 사용하고 있지만 필요한 경우 다른 것으로 이동할 수 있습니다.


4
정말로 이것이 필요합니까? 다른 사람들이 지적한대로 반드시 (A) 사용자 정의 클래스를 생성 할 수는 있지만 런타임에서 사용하는 방법을 알아야합니다 (B). 부분 (B)도 나에게 많은 일인 것 같습니다. DataSet 객체 또는 사전과 같은 일종의 컬렉션에 데이터를 유지하는 데 어떤 문제가 있습니까? 무엇을하려고합니까?
Job

Rob Conery가 dynamicMassive 에서 수행 한 작업을 살펴보십시오 . blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Robert Harvey

1
파이썬은 동적 클래스 선언을 허용하지만 실제로는 일반적입니다. 2001 년경부터 David Mertz의 튜토리얼이있었습니다 (검색했지만 정확한 링크를 찾지 못했습니다). 간단합니다.
smci

@RobertHarvey 귀하가 공유 한 링크가 죽었습니다. 어디서 찾을 수 있는지 아십니까?
Joze

1
기술적으로는 가능하지만 그렇게하는 것의 가치에 의문을 제기해야합니다. 강력한 타이핑 및 클래스의 요점은 컴파일 타임에 클래스 정보를 사용하여 구문 오류를 확인할 수 있도록하는 것입니다 (신세대 동적 JITer는이를 사용하지 않고도 최적화 할 수 있음). 강력하게 입력 된 환경에서 동적 타이핑을 사용하면 분명히 패러다임의 모든 장점을 잃게됩니다.
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
메타 프로그래밍 참조에 감사드립니다. 여기 내가 찾은 다른 것들이 있습니다
Leo Gurdian
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.