Autor: elGuille

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

     

    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 C# está en ese mismo artículo)

     

    Namespace ActivatorVB
    
        Public NotInheritable Class CrearClase
    
            Public Shared Sub Main()
    
                'CrearClase2.Prueba()
    
                'Main2()
    
                Prueba()
    
                Console.ReadKey()
    
            End Sub
    
            Private Shared Sub Prueba()
                ' La clase a crear
                Dim elNombre = "ActivatorVB.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 VB utilizo TryCast en lugar
                ' de DirectCast o CType
                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)
    
                '' Esta no tiene constructor
                'elNombre = "OtroEspacio.Coleguilla2"
                '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 = "ActivatorVB.ColegaAbstracto"
                    crearColega(elNombre)
    
                Catch ex As Exception
                    Console.WriteLine("Mrw! (o Ups!) esta clase {0} da error: {1}",
                                      elNombre, ex.Message)
    
                End Try
    
            End Sub
    
            Private Shared Sub crearColega(laClase As String)
                Dim elTipo As Type = Type.GetType(laClase)
    
                If Not GetType(Colega).IsAssignableFrom(elTipo) Then
                    Console.WriteLine("El tipo: {0} no es del tipo adecuado." & vbLf & "",
                                      laClase)
                    Return
    
                End If
    
                'Dim obj1 As Object = Activator.CreateInstance(elTipo)
                'Dim obj2 As Colega = CType(Activator.CreateInstance(elTipo), Colega)
                'Dim obj3 As Colega = DirectCast(Activator.CreateInstance(elTipo), Colega)
    
                Dim obj As Colega = TryCast(Activator.CreateInstance(elTipo), Colega)
    
                If obj IsNot Nothing Then
                    obj.Nombre = "Pepe"
                    obj.Email = "pepe@outlook.com"
    
                    Console.WriteLine("Objeto creado correctamente: {0}{1}{2}, {3}",
                                      obj.GetType().Name, vbCrLf,
                                      obj.Nombre, obj.Email)
                Else
                    Console.WriteLine("Parece que el tipo: {0} no es del tipo adecuado.",
                                      laClase)
                End If
    
                Console.WriteLine()
            End Sub
    
            Sub pruebaDirecta()
                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
    
            End Sub
    
            Shared Sub Main2()
                Dim laClase = "ActivatorVB.unColega"
    
                Dim elTipo As Type = Type.GetType(laClase)
    
                Dim obj As IColega = TryCast(Activator.CreateInstance(elTipo), IColega)
    
                If obj IsNot Nothing Then
                    obj.Nombre = "Pepe"
                End If
    
            End Sub
    
        End Class
    
        Public Interface IColega
            Property Nombre As String
        End Interface
    
        Public Class unColega
            Implements IColega
    
            Public Property Nombre As String Implements IColega.Nombre
        End Class
    
        Public Class Colega
            Public Property Nombre As String
            Public Property Email As String
        End Class
    
        ' Clase no instanciable
        Public MustInherit Class ColegaAbstracto
            Inherits Colega
    
            Public Property Edad As Integer
        End Class
    
    End Namespace
    
    Namespace OtroEspacio
    
        Public Class Colega
            Public Property Nombre As String
            Public Property Edad As Integer
        End Class
    
        Public Class Coleguilla
            Inherits ActivatorVB.Colega
    
            Public Property Edad As Integer
    
        End Class
    
        ' las clase selladas (sealed / NotInheritable)
        ' se pueden usar, pero siempre deben tener 
        ' un constructor público sin parámetros
        Public NotInheritable Class Coleguilla2
            Inherits ActivatorVB.Colega
    
            'Protected Sub New()
            'End Sub
    
            'Private Sub New()
            'End Sub
    
            Public Property Edad As Integer
        End Class
    
    End Namespace
    
    
    

     

    Nos vemos.

    Guillermo

    P.S. (06/Ene/13)

    He actualizado el código que esta entrada la edité desde el editor de WordPress y se hizo un follón, quitando los espacios y el formato… GRRRR!

    …

  • 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

  • hay que ver… como es el Format con las llaves…

     

    Pues eso… que si usas Format, en cualquiera de sus encarnaciones: String.Format, StringBuilder.AppendFormat, Console.WriteLine, (incluso la función Format de Visual Basic) etc. y estas usando llaves para indicar parámetros (marcadores de posición que dicen en la documentación), mucho cuidado con que la cadena que estás formateando no tenga también llaves, porque entonces la hemos "pifiao".

     

    El problema

     

    Por ejemplo, si tienes esta cadena (o este código XAML):

     

    Dim xaml = <StackPanel Style="{StaticResource spImagen}">
                   <Image Source="ms-appx:///contenido/img/{0}"
                       Style="{StaticResource Imagen}"/>
               </StackPanel>
    
    

     

    Y pretendes usarlo con algo como esto:

    VB:

     

    sb.AppendFormat(xaml.ToString & "{1}",
                    sText.Replace(".tif", ".jpg"), vbCrLf)
    

     

    C#:

    Nota:

    En C# no se puede usar el contenido XML directamente, pero no es complicado adaptarlo, eso sí, es algo más laborioso que en Visual Basic, todo hay que decirlo. Pero para este caso que es poco código lo pasaremos por alto 😉

     

    string xaml = @"<StackPanel Style=""{StaticResource spImagen}"">
                        <Image Source=""ms-appx:///contenido/img/{0}""
                            Style=""{StaticResource Imagen}""/>
                    </StackPanel>";
    
    sb.AppendFormat(xaml + "\n",
                    sText.Replace(".tif", ".jpg"));
    

     

    Si usamos este código (ya sea con VB o con C#) el compilador producirá una excepción de formato no válido (FormatException) y es porque el "formateador" se encuentra con llaves (de inicio y/o de cierre) que no tienen un valor numérico asignado.

     

    FormatException

    Figura 1. El error en la aplicación de C# (es el mismo que en VB)

     

     

    La solución

     

    Pues… quitar las cadenas con las llaves que no sean "marcadores" y agregarlas en el propio AppendFormat:

     

    VB:

     

    Dim xaml = <Paragraph TextAlignment="Center">
                   <InlineUIContainer>
                       <StackPanel Style="{0}">
                           <Image Source="ms-appx:///contenido/img/{1}"
                               Style="{2}"/>
                       </StackPanel>
                   </InlineUIContainer>
               </Paragraph>
    
    sb.AppendFormat(xaml.ToString & "{3}",
                    "{StaticResource spImagen}",
                    sText.Replace(".tif", ".jpg"),
                    "{StaticResource Imagen}", vbCrLf)
    

     

    C#:

     

    string xaml = @"<StackPanel Style=""{0}"">
                        <Image Source=""ms-appx:///contenido/img/{1}""
                            Style=""{2}""/>
                    </StackPanel>";
    
    sb.AppendFormat(xaml + "\n","{StaticResource spImagen}",
                    sText.Replace(".tif", ".jpg"), "{StaticResource Imagen}");
    

     

    Es decir, ponemos para cada una de las "llaves fatídicas" una cadena con un marcador (de posición) y ponemos esas cadena (con sus llaves) en los parámetros de la función Format y asunto solucionado.

     

    Nota del Guille:

    Mira que esto mismo ya me pasó ayer (o hace un puñao de horas) y después me volvió a pasar, pero he estado un buen rato dándole vueltas al tema (se me olvidó que volví a cambiar el código XAML) y no daba con el dichoso error… eso me pasa por dos razones:

    1.- no tener buena memoria, no dormir lo suficiente, ser más cabezón que el Visual Studio

    2.- no usar suficientes Try/catch para "acorralar" el error…

    En fin… lo importante es que he dado con el error y que te lo cuento por si te pasa a ti.

     

    Si todo va bien, ya mismo te explicaré para qué estoy usando ese código (el de Visual Basic, ya que el de C# sólo lo he escrito para aquellos que les gustan los puntos y comas)…

     

    Lo dicho, espero que te pueda ser de ayuda… esa siempre es la intención 😉

     

    Nos vemos.

    Guillermo

    …