Etiqueta: truco

  • Si utilizas dynamic en C# acuérdate de agregar una referencia a Microsoft.CSharp

     

    Pues eso… que estaba haciendo la prueba de usar Activator sin indicar el tipo al que quiero convertir una cadena y me daba error indicando que: "one or more types required to compile a dynamic expression cannot be found" y claro, ni idea… ya que suponía que eso de usar dynamic en C# era así, simplemente indicando que el tipo es dynamic y ya está. Pero no, debes tener una referencia a Microsoft.CSharp:

    dynamic necesita una referencia a Microsoft.CSharp

     

    De paso, te pongo el ejemplo de cómo instanciar una clase desde el contenido de una cadena, pero sin tener que hacer la conversión (o cast) a un tipo concreto.

    Este ejemplo es una especie de ampliación de Crear un objeto a partir de una cadena con Activator.CreateInstance

     

    private static void crearClase(string laClase)
    {
        var todoBien = true;
        dynamic obj = null;
    
        Type elTipo = Type.GetType(laClase);
        try
        {
            obj = Activator.CreateInstance(elTipo);
    
            if (obj != null)
            {
                obj.Nombre = "Pepe";
                obj.Email = "pepe@outlook.com";
    
                Console.WriteLine("Objeto creado correctamente: {0}{1}{2}, {3}",
                                  obj.GetType().Name, "\r\n",
                                  obj.Nombre, obj.Email);
            }
    
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message);
            todoBien = false;
        }
    
        if (!todoBien)
        {
            Console.WriteLine("Parece que el tipo: {0} no es del tipo adecuado.",
                              laClase);
        }
    
        Console.WriteLine();
    }
    
    

     

     

    Espero que te sea de utilidad.

    Nos vemos.

    Guillermo

  • Crear un objeto a partir de una cadena con Activator.CreateInstance

     

    Pues eso… como el título indica, te voy a explicar cómo crear (instanciar sería más correcto) un objeto a partir del "nombre" de una clase, pero estando ese nombre dentro de una cadena. Y como de costumbre, con el código de ejemplo tanto para (mi querido) Visual Basic como para (mi obligado) C# 😉

    Lo de "obligado" también lo puedes tomar por "obrigado" que en portugués significa "gracias", de agradecimiento, no te vayas a pensar que el C# me hace gracia… bueno, algunas veces me hago a la idea que me está guiñando… (por lo del punto y coma) 😉

     

    Vamos al tema.

    Si tienes una cadena con el nombre de una clase, puedes crear una nueva instancia de esa clase utilizando la clase Activator, más concretamente el método compartido CreateInstance.

    Para que funcione la conversión de cadena a objeto del tipo correcto debes tener en cuenta que:

    1. Indiques el espacio de nombres del tipo en la cadena
    2. La clase puede ser una clase derivada (o implementada)
    3. El tipo tenga un constructor público y sin parámetros
    4. Las clases abstractas (abstract / MustInherit) no sirven ya que no se pueden instanciar
    5. Que la clase a crear esté accesible, vamos que si tú no puedes crear el objeto ni indicando el tipo de forma explícita el Activator no creo que haga milagros…

     

    Una vez que sabemos las condiciones que debemos seguir para instanciar clases a partir del contenido de un string vamos a ponerlo a prueba con unos ejemplos en los que comprobaremos lo dicho en la lista anterior y algo más.

    El código para hacer la conversión lo podemos simplificar al máximo, pero en ese caso sólo deberíamos usarlo para crear clases que cumplan las 2 primeras condiciones indicadas en la lista anterior.
    Para casos indicados en los puntos 3 y 4 habría que usar un bloque try/catch para detectar la excepción que ocurrirá, ya que el tipo es correcto pero no se puede instanciar.
    Para los casos que cumplan la última condición (el tipo no existe o no está accesible) podemos usar una comprobación que en realidad es para saber si el tipo es del mismo tipo a convertir o se deriva de él, esa comprobación la haremos por medio del método IsAssignableFrom (definido en la clase Type).

    Veamos primero el código que hace la conversión sin más comprobaciones 
    (el código completo lo he puesto en un método para que sea más fácil hacer varias de las pruebas).

    VB:

    Dim laClase = "ActivatorVB.Colega"
    
    Dim elTipo As Type = Type.GetType(laClase)
    
    Dim obj As Colega = TryCast(Activator.CreateInstance(elTipo), Colega)
    
    If obj IsNot Nothing Then
        obj.Nombre = "Pepe"
        obj.Email = "pepe@outlook.com"
    End If
    
    

     

    C#:

    var laClase = "ActivatorCS.Colega";
    
    Type elTipo = Type.GetType(laClase);
    
    Colega obj = Activator.CreateInstance(elTipo) as Colega;
    
    if (obj != null)
    {
        obj.Nombre = "Pepe";
        obj.Email = "pepe@outlook.com";
    }
    

     

    Un par de aclaraciones (tanto para VB como para C#, las aclaraciones) sobre este código:

    En VB utilizo TryCast y en C# utilizo as para hacer la conversión ya que de esa forma, si el objeto no se puede crear se devuelve un valor nulo.

    Si en VB utilizara CType o DirectCast o en C# hiciera la conversión con (Colega) y la conversión falla, produciría un error.

    Aunque en realidad esto sólo nos salvará de los casos indicados en el punto 5 de las "condiciones" para realizar la instanciación de la clase a partir de una cadena. Pero bueno, ya sabes que hay varias formas de hacer conversiones entre tipos 😉

     

    Para hacer la comprobación de si el tipo es adecuado por medio del método IsAssignableFrom lo haríamos de la siguiente forma:

     

    '  VB
    ' Esto controlará las clases que no se basan en Colega
    If Not GetType(Colega).IsAssignableFrom(elTipo) Then Console.WriteLine("El tipo: {0} no es del tipo adecuado." & vbLf & "", laClase) Return End If // C# // Esto controlará las clases que no se basan en Colega if (!typeof(Colega).IsAssignableFrom(elTipo)) { Console.WriteLine("El tipo: {0} no es del tipo adecuado.\n", laClase); return; }

     

    Ya solo me queda ponerte todo el código de prueba en el que he hecho las pruebas esas que conforman la lista para estar seguro de que es así…

    Te recomiendo que hagas tú las tuyas propias, incluso usando las partes comentadas en dicho código.

    En el método Main2 de dicho código he puesto un ejemplo de una clase que implementa una interfaz.

     

    Nota:

    El código completo lo pongo aparte para que no llene tanto esto.

    Además te pongo separado el de Visual Basic de el de C# y tu eliges el que quieras.

    También está publicado en Pastebin.

     

    Espero que te sea de utilidad.

    Nos vemos.

    Guillermo

    P.S.

    Los enlaces:

    El de VB en Pastebin

    El de C# en Pastebin

    El de VB en mi blog

    El de C# en mi blog

    elGuille_luna300x200

  • Código de cómo instanciar una clase a partir del contenido de una cadena (C#)

     

    Lo dicho el código completo para Visual Basic del ejemplo comentado en:

    Crear un objeto a partir de una cadena con Activator.CreateInstance

    (el enlace al de Visual Basic está en ese mismo artículo)

     

    //-----------------------------------------------------------------------------
    // Crear un objeto a partir de una cadena con Activator             (05/Ene/13)
    //
    //
    // ©Guillermo 'guille' Som, 2013
    //-----------------------------------------------------------------------------
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace ActivatorCS
    {
        public sealed class CrearClase
        {
    
            public static void Main()
            {
                // La clase a crear
                var elNombre = "ActivatorCS.Colega";
    
                crearColega(elNombre);
    
    
                // Esta clase se llama igual,
                // pero no es del mismo tipo
                // y no pasaría la prueba de IsAssignableFrom,
                // aunque no daría error al hacer la conversión
                // pero no crearía una instancia,
                // por tanto Activator devolvería nulo,
                // en realidad el valor nulo lo devuelve
                // la operación de conversión de tipos
                // por eso en C# utilizo "as Colega" en lugar de "(Colega)"
                // para hacer la conversión
                elNombre = "OtroEspacio.Colega";
                crearColega(elNombre);
    
                // esta clase se basa en la que queremos crear
                // aunque esté en otro espacio de nombres
                elNombre = "OtroEspacio.Coleguilla";
                crearColega(elNombre);
    
                // este tipo no existe,
                // pero debemos usar IsAssignableFrom
                // para no provocar una excepción
                elNombre = "Activator.Colega";
                crearColega(elNombre);
    
                // esta se basa en la nuestra
                // pero es abstracta (no tiene constructor)
                // ya dará un error al crearla
                // aunque pasaría la prueba de IsAssignableFrom
                try
                {
                    elNombre = "ActivatorCS.ColegaAbstracto";
                    crearColega(elNombre);
    
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Mrw! (o Ups!) esta clase {0} da error: {1}",
                                      elNombre, ex.Message);
    
                }
    
                Console.ReadKey();
    
            }
    
            private static void crearColega(string laClase)
            {
                Type elTipo = Type.GetType(laClase);
    
                // el equivalente en c# de TryCast es hacer el cast con "as tipo"
                // aunque también se puede comprobar si se puede crear el objeto
    
                // Esto contralará las clases que no se basan en Colega
                if (!typeof(Colega).IsAssignableFrom(elTipo))
                {
                    Console.WriteLine("El tipo: {0} no es del tipo adecuado.\n", 
                                      laClase);
                    return;
                }
    
                Colega obj = Activator.CreateInstance(elTipo) as Colega;
    
                if (obj != null)
                {
                    obj.Nombre = "Pepe";
                    obj.Email = "pepe@outlook.com";
    
                    Console.WriteLine("Objeto creado correctamente: {0}{1}{2}, {3}",
                                      obj.GetType().Name, "\r\n",
                                      obj.Nombre, obj.Email);
                }
                else
                {
                    Console.WriteLine("Parece que el tipo: {0} no es del tipo adecuado.", 
                                      laClase);
                }
    
                Console.WriteLine();
            }
    
            void pruebaDirecta()
            {
                var laClase = "ActivatorCS.Colega";
    
                Type elTipo = Type.GetType(laClase);
    
                Colega obj = Activator.CreateInstance(elTipo) as Colega;
    
                if (obj != null)
                {
                    obj.Nombre = "Pepe";
                    obj.Email = "pepe@outlook.com";
                }
    
            }
    
            static void Main2()
            {
                var laClase = "ActivatorVB.unColega";
    
                Type elTipo = Type.GetType(laClase);
    
                IColega obj = ((IColega)Activator.CreateInstance(elTipo));
    
                if (obj != null)
                {
                    obj.Nombre = "Pepe";
                }
            }  
    
        }
    
        public interface IColega
        {
            string Nombre { get; set; }
        }
    
        public class unColega : IColega
        {
            public string Nombre { get; set; }
        }
    
    
        public class Colega
        {
            public string Nombre { get; set; }
            public string Email { get; set; }
        }
    
        // Clase no instanciable
        public abstract class ColegaAbstracto : Colega
        {
            public int Edad { get; set; }
        }
    
    }
    
    namespace OtroEspacio
    {
    
        public class Colega
        {
            public string Nombre { get; set; }
            public int Edad { get; set; }
        }
    
        public class Coleguilla : ActivatorCS.Colega
        {
            public int Edad { get; set; }
        }
    
        // las clase selladas (sealed / NotInheritable)
        // se pueden usar, pero siempre deben tener
        // un constructor público sin parámetros
        public sealed class Coleguilla2 : ActivatorCS.Colega
        {
    
            //protected Coleguilla2()
            //{ 
            //}
    
            //private Coleguilla2()
            //{
            //}
    
            public int Edad { get; set; }
        }
    
    }
    

     

    Nos vemos.

    Guillermo