Generar una clase dinámicamente a partir de tipos que se obtienen en tiempo de ejecución

20

¿Es posible hacer lo siguiente en C # (o en cualquier otro idioma)?

  1. Estoy obteniendo datos de una base de datos. En tiempo de ejecución puedo calcular el número de columnas y los tipos de datos de las columnas recuperadas.

  2. A continuación, quiero "generar" una clase con estos tipos de datos como campos. También quiero almacenar todos los registros que obtuve en una colección.

El problema es que quiero hacer los pasos 1 y 2 en tiempo de ejecución

es posible? Actualmente estoy usando C # pero puedo cambiar a otra cosa si lo necesito.

Chani
fuente
44
¿Realmente necesitas esto? Seguramente puede (A) generar una clase personalizada como otros señalaron, pero también necesita (B) saber cómo usarla en tiempo de ejecución. La parte (B) también me parece mucho trabajo. ¿Qué tiene de malo mantener los datos dentro del objeto DataSet o algún tipo de colección como el diccionario? ¿Que estás tratando de hacer?
Trabajo
Asegúrese de echar un vistazo al trabajo que hizo Rob Conery dynamicen Massive: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Robert Harvey
1
Python permite la declaración de clase dinámica, y de hecho es común. Hubo un tutorial de David Mertz de alrededor de 2001 (lo busqué pero no pude encontrar el enlace exacto). Es sencillo.
smci
@RobertHarvey El enlace que compartiste está muerto. ¿Sabes dónde puedo encontrarlo?
Joze
1
Aunque técnicamente posible, tendría que cuestionar el valor de hacerlo. El objetivo de la escritura y las clases seguras es que, en el momento de la compilación, puede utilizar la información de la clase para verificar si hay errores de sintaxis (la generación más nueva de JIT dinámicos puede optimizar sin esto). Claramente, al tratar de usar la escritura dinámica en un entorno fuertemente tipado significaría que perderá todas las ventajas de cualquiera de los paradigmas ...
ArTs

Respuestas:

28

Use CodeDom. Aquí hay algo para comenzar

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

Crea un ensamblado 'Test.Dynamic.dll' con esta clase en él.

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

También puede usar características dinámicas de C #

Clase DynamicEntity, no es necesario crear nada en tiempo de ejecución

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

Y úsalo así

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
fuente