• 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!

    …

  • 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

    …

  • Presuntamente algunas funciones de Microsoft.VisualBasic no son compatibles con las aplicaciones para Windows Store

     

    Pues eso… que en un proyecto DLL (biblioteca de clases) para Windows Store no me ha reconocido ciertas funciones de manipulación de cadenas que están definidas en el módulo Strings del espacio de nombres Microsoft.VisualBasic (que es como si dijéramos el "runtime" de Visual Basic para .NET).

    Lo de presuntamente es por si he hecho algo mal y por eso no me reconocía algunas funciones… al menos en un proyecto del tipo biblioteca de clases (class library), en la figura 1 tienes las cosas que se pueden usar de ese módulo con las funciones de cadena.

     

    Microsoft.VisualBasic.Strings en app store class library
    Figura 1. Las funciones que muestra Strings en un proyecto del tipo Class library para Windows store

     

    Nota:
    En una aplicación para Windows Store (no una librería de clases) también muestra los mismos métodos. En una aplicación/librería para el escritorio (incluso de Windows 8) muestra las que estamos habituados a usar.

     

    Así que… me las he tenido que fabricar, al menos las que me han hecho falta para este caso concreto, que no es otro que una actualización de mi librería (perdón, biblioteca) para colorear código: gsColorearCodigo.

    Aquí te dejo un módulo de Visual Basic para hacer las conversiones… lo mismo ya tenía algo publicado sobre algo parecido, pero…

     

    '------------------------------------------------------------------------------
    ' VBStrings                                                         (01/Ene/13)
    '
    ' Para simular las funciones de cadena de Visual Basic
    ' ya que parece que el runtime para Windows Store no incluye algunas funciones
    '
    ' ©Guillermo 'guille' Som, 2013
    '------------------------------------------------------------------------------
    Option Strict On
    Option Infer On
    
    
    Public Module VBStrings
        ''' <summary>
        ''' Devuelve la posición (en base 1) de cadena1 en cadena2
        ''' </summary>
        ''' <remarks></remarks>
        Public Function InStr(cadena1 As String, cadena2 As String) As Integer
            Try
                Dim i = cadena1.IndexOf(cadena2)
                Return i + 1
            Catch ex As Exception
                Return 0
            End Try
        End Function
    
    
        ''' <summary>
        ''' Devuelve la posición en base 1 de cadena1 en cadena2
        ''' se comprueba a partir de la posición desde
        ''' </summary>
        ''' <remarks></remarks>
        Public Function InStr(desde As Integer, cadena1 As String, cadena2 As String) As Integer
            Try
                Dim i = cadena1.IndexOf(cadena2, desde - 1)
                Return i + 1
            Catch ex As Exception
                Return 0
            End Try
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena con los cuantos primeros caracteres de cadena1
        ''' El primer caracter es el 1 y se devuelve en base 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Left(cadena1 As String, cuantos As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse cuantos < 1 Then
                Return ""
            End If
    
            Dim s = cadena1.Substring(0, cuantos - 1)
            Return s
        End Function
    
        ''' <summary>
        ''' Devuelve la cantidad de caracteres que tiene cadena1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Len(cadena1 As String) As Integer
            If String.IsNullOrWhiteSpace(cadena1) Then
                Return 0
            End If
    
            Return cadena1.Length
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena nueva después de quitarle
        ''' los espacios del principio y del final
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Trim(cadena1 As String) As String
            If String.IsNullOrWhiteSpace(cadena1) Then
                Return ""
            End If
    
            Return cadena1.Trim()
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena a partir de la posición desde
        ''' La pirmera posición es la 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Mid(cadena1 As String, desde As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse desde < 1 Then
                Return ""
            End If
    
            Return cadena1.Substring(desde - 1)
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena con cuantos caracteres de cadena1 
        '''  a partir de desde
        ''' La primera posición es la 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Mid(cadena1 As String, desde As Integer, 
                                                cuantos As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse desde < 1 Then
                Return ""
            End If
    
            Return cadena1.Substring(desde - 1, cuantos)
        End Function
    
        ''' <summary>
        ''' Trocea una cadena usando los caracteres indicados
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Split(cadena1 As String, caracteres As String) As String()
            Return cadena1.Split(caracteres.ToCharArray, 
                                  StringSplitOptions.RemoveEmptyEntries)
        End Function
    End Module
    
    
    

     

    Espero que te sea de utilidad.

     

    Nos vemos.

    Guillermo

    …