sábado, 29 de abril de 2017

Interfaces predefinidos en .NET

Interfaces genéricas de .NET


. NET define varias interfaces genéricas de uso común de entre las que podemos elegir para implementar nuestras clases. Estas interfaces nos permiten obtener funcionalidad adicional para nuestro objeto. Algunas interfaces se describen en la tabla siguiente:



Interfaz


Descripción

IComparable

Define el ordenamiento de instancias de la clase. Es útil si queremos utilizar la clase como un valor clave en SortedList o vamos a usar el método Sort del ArrayList.


IEnumerable e IEnumerator


Estas dos interfaces trabajan juntas dando soporte a los For…Each de nuestra clase


IFormattable


Permite definir formateos de cadena definidos por el usuario


IList


Permite a la clase servir como fuente de datos Para controles tales como ListBox y DataGrid. La implementación de la base es List.


ICloneable


Permite definir exactamente como será copiado nuestro objeto.


IComponent


Provee soporte como componente a nuestra clase en tiempo de diseño. La base de esta implementación es Component.


IDataErrorInfo


Permite añadir a la clase información sobre errores.  El soporte de esta interface nos permite usar el control Dataerror en un formulario de Windows.



A continuación, vamos a crear una clase simple que representa un punto. Vamos a usar esta clase como una base para la aplicación de los interfaces IComparable, IEnumerator IEnumerable, y de IFormattable. Estas interfaces de usuario hacen que la clase sea más fácil de usar para otros desarrolladores.

Interfaces predefinidos en .NET


Implementando el interface IComparable


La interfaz IComparable permite definir un orden de las instancias de clase. Si la clase representa un objeto que lleva a una interpretación significativa de más o menos, primero o el último, o más grande o más pequeño, es razonable para definir la interfaz IComparable para nuestra clase.
IComparable tiene uno de los miembros, el método CompareTo. En este ejemplo, se implementa una clase que representa un punto y compara puntos basándose en la distancia desde el origen.

Crear la clase PuntoOrdenable


1. Crear un Nuevo proyecto Windows application  y llamarlo Puntos
2. Añadir una clase llamada CPuntoOrdenable.
3. Añadir las propiedades X e Y a la clase  CPuntoOrdenable.

Public Class CPuntoOrdenable
    Private m_x As Integer = 0
    Public Property X() As Integer
        Get
            Return (m_x)
        End Get
        Set(ByVal Value As Integer)
            m_x = Value
        End Set
    End Property

    Private m_y As Integer = 0
    Public Property Y() As Integer
        Get
            Return (m_y)
        End Get
        Set(ByVal Value As Integer)
            m_y = Value
        End Set
    End Property
End Class

Añadir el Interface IComparable


1. Añadir el interface IComparable a la declaración de la clase:

Implements IComparable

2. Hacer click sobre  IComparable con lo que se escribirá en el código automáticamente
La definición de CompareTo.

Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo

End Function

El método CompareTo compara la primera instancia de la una clase, Me con otra instancia de la clase, Advertencia: Si las dos instancias son iguales, de acuerdo con definición de la clase de la clasificación, CompareTo devuelve 0. Si la instancia Me es más grande (la instancia comparada ocupa el segundo lugar), CompareTo devuelve un entero positivo. Si  Me es más pequeño (la instancia comparada es más grande y por tanto está delante de Me), CompareTo devuelve un entero negativo.

 Agregar  código a la función CompareTo para crear una función auxiliar, DistanciaCuadrado. Los puntos se compararon dos a la vez, así  que no
importa qué valor devuelva el método
CompareTo, siempre y cuando  retorne
un número positivo si 
Me  está  más lejos que el otro punto. El método DistanciaCuadrado devuelve el cuadrado  de la distancia del punto desde el origen. Si se utiliza el método CompareTo la distancia real para comparar distancias, el código tendría que
trabajar con valores
System.Double. Trabajar con valores System.Double
tiene tres desventajas. En primer lugar, afecta negativamente el rendimiento.
En segundo lugar, debido a la forma como se almacenan, es más trabajosa para probar
la equivalencia. En tercer lugar, el código tiene que convertir la diferencia de nuevo en
un número entero, el tipo de retorno de
CompareTo.

Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo

Return (Me.DistanciaCuadrado() - CType(obj,CPuntoOrdenable).DistanciaCuadrado())

End Function

Private Function DistanciaCuadrado() As Integer
Return (m_x * m_x) + (m_y * m_y)
End Function

'Constructor
Public Sub New(ByVal x As Integer, ByVal y As Integer)
        m_x = x
        m_y = y
End Sub

Testear el interfaz


Una forma sencilla de probar la interfaz IComparable sería la creación de una lista de
puntos, ordenarlos, y luego imprimir los puntos ordenados para comprobar que están en orden.
En lugar de eso, este procedimiento genera un grupo de puntos colocados al azar. Pintaremos los puntos del formulario con diferente densidad de color  y quedarán clasificados según  varía dicha densidad de color.

1. Abrir el Form1 y arrastrar sobre él un botón. Poner sobre la propiedad Texto la palabra Dibujar.

2.Agregar este código al evento click del botón para generar puntos, ordenarlos, y  mostrarlos en el formulario. El método de ArrayList Ordenar utiliza el método Comparable.CompareTo para clasificar las instancias de CPuntoOrdenable. La intensidad del color depende de la posición del punto entre el resto de puntos ordenados. Cuanto más alto
el valor, menos intenso será el color.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim puntos As New ArrayList()
      Dim rgen As New System.Random()
      Dim pt As CPuntoOrdenable
      Dim count As Integer
      Dim pintar As Graphics = Me.CreateGraphics
      Dim aColor As Color

      For count = 0 To 249
        puntos.Add(New CPuntoOrdenable(rgen.Next(200),rgen.Next(200)))
      Next

      puntos.Sort()

      For count = 0 To 249
         pt = CType(puntos(count), CPuntoOrdenable)
         aColor = System.Drawing.Color.FromArgb(25, 25, count)
         Dim brush As New System.Drawing.SolidBrush(aColor)
         pintar.FillEllipse(brush, pt.X, pt.Y, 10, 10)
         brush.Dispose()
      Next
End Sub

3. Presionar F5 para ejecutar el programa. Cada vez que se haga clic en el botón Dibujar, se añadirán al formulario 250 puntos más. Debido a que los puntos no están dibujados en
el evento Paint, los puntos no se quedarán en el formulario si se minimiza y  luego se maximiza.

interfaces genéricos con .NET



Para obtener una clara indicación de que los puntos están siendo
ordenados, comentar la llamada a ordenar y, a continuación, ejecutar el programa.

interfaces genéricas con .NET

 Implementando los interfaces IEnumerable y IEnumerator


En el ejemplo anterior, se  han generado puntos, añadiéndolos a un objeto ArrayList,
y ordenándolos y luego dibujándolos en el formulario. En el siguiente ejemplo, se crea una clase,
CListaPuntoOrdenado, que contiene un grupo de puntos. En lugar de utilizar un bucle para acceder a los miembros de la clase ArrayList y luego modificar cada elemento con el tipo CPuntoOrdenado, seremos capaces de utilizar un bloque  For Each, que devuelve sólo un objeto CPuntoOrdenado.

La interfaz
IEnumerable tiene uno de los miembros, el método GetEnumerator. El
Método
GetEnumerator devuelve una instancia de una clase que implementa la interfaz IEnumerator. La interfaz IEnumerator tiene tres miembros, y restablece el método MoveNext y la propiedad actual. Los tres miembros trabajan juntos para enumerar los
miembros de la clase
CListaPuntoOrdenado como se puede ver aquí.

Dim enumerator As IEnumerator = puntos.GetEnumerator()
While enumerator.MoveNext
      pt = CType(enumerator.Current, CPuntoOrdenable)
End While

Crear la clase CListaPuntoOrdenado


1. Añadir una nueva clase al proyecto, llamada CListaPuntoOrdenado.

2. Modificar la clase añadiendo el interfaz IEnumerable:

Public Class CListaPuntoOrdenado
    Implements IEnumerable
End Class

3. Añadimos el constructor

Public Sub New()
End Sub

usando System.Collections;

4. Añadir una instancia de ArrayList y un método llamado  AgregarPuntosAleatorios el cual añade un número de puntos generados aleatoriamente a una clase ArrayList. Este código es similar al de la sección anterior.

Private m_puntos As New ArrayList()
Public Sub AddRandomPoints(ByVal howMany As Integer, ByVal maximum As Integer)

m_puntos.Clear()
      Dim rgen As New System.Random()
      Dim count As Integer
      For count = 0 To howMany - 1 m_puntos.Add(New CPuntoOrdenable(rgen.Next(maximum), rgen.Next(maximum)))
        Next
        m_puntos.Sort()
End Sub

Anadir la clase interna IEnumerator


1. Declarar una nueva clase dentro de la clase de CListaPuntoOrdenado y llamarla EnumeradorPuntos para que implemente el interfaz del IEnumerator.

Private Class CEnumeradorPuntos
        Implements IEnumerator

Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
            Get

            End Get
       End Property

Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext

       End Function

Public Sub Reset() Implements System.Collections.IEnumerator.Reset

        End Sub
End Class


La clase CEnumeradorPuntos  se llama clase interna, ya que está definida dentro de otra clase. Una instancia de esta clase se crea y se devuelve por el método GetEnumerator. El usuario de la instancia CEnumeradorPuntos necesita conocer sólo la clase que implementa la interfaz IEnumerator. Así la única clase que necesita saber acerca de la clase CEnumeradorPuntos es la clase CListaPuntoOrdenado.

2.Hacer click en IEnumerator y se despliegan los métodos del interface a implementar

Private Class CEnumeradorPuntos
Implements IEnumerator

Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
            Get

            End Get
      End Property

Public Function MoveNext() As Boolean Implements       System.Collections.IEnumerator.MoveNext
      Implements System.Collections.IEnumerator.MoveNext      End Function

Public Sub Reset() Implements System.Collections.IEnumerator.Reset

      End Sub
End Class

3. Añadir estos campos a la clase CEnumeradorPuntos clase. El primero se refiere
a la colección
m_puntos de la instancia CListaPuntoOrdenado.  m_posicion es la posición actual en la enumeración. m_CuentaInicial es el recuento de puntos en m_puntos cuando el enumerador es instanciado. Por convención en .NET Framework, el
MoveNext  y el Current del Enumerador deben fallar si la colección enumerada cambia durante la enumeración. En este ejemplo, vamos a usar la cuenta inicial de puntos para comprobar si la clase ArrayList  ha cambiado.

Dim m_puntos As ArrayList
Dim m_posicion As Integer = -1
Dim m_cuentainicial As Integer

4. Añadir  esto al constructor.

Public Sub New(ByVal puntos As ArrayList
m_puntos = puntos
      m_cuentainicial = puntos.Count
End Sub

5. Añadir este código para el método Reset. Se implementa mediante el enumerador
mediante la adición de 1 a
m_posicion con cada llamada a MoveNext. La propiedad Current devuelve el elemento en los m_puntos de ArrayList al índice m_posicion.
Debido a que el primer valor de la enumeración se encuentra llamando al método MoveNext, el método Reset necesita mantener el valor del índice a la derecha antes del primer elemento de la clase ArrayList. El  primer elemento en  ArrayList en el índice es 0, por lo que el método Reset establece m_posicion a -1.

Public Sub Reset() Implements System.Collections.IEnumerator.Reset
m_posicion = -1
End Sub

6. Agregar el siguiente código para el método MoveNext. La primera prueba determina si las instancias de CPuntoOrdenable se han añadido o eliminado a m_puntos en el ArrayList. Si no, m_posicion se incrementa. En caso contrario, el código produce una excepción, InvalidOperationException.

Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
If (m_cuentainicial = m_puntos.Count) Then
            m_posicion += 1
            If (m_posicion >= m_puntos.Count) Then
                  Return (False)
            Else
                  Return (True)
            End If
       Else
Throw New InvalidOperationException("La Coleccion ha cambiado durante la enumeration.")
       End If
End Function

7. Añadir este código a la propiedad Current.

Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
Get
            If (m_cuentainicial <> m_puntos.Count) Then
Throw New InvalidOperationException("La Colección ha cambiado durante la enumeration.")
            ElseIf (m_posicion >= m_puntos.Count) Then
                    Throw New InvalidOperationException("El valor de      la Enumeration es invalido.")
            Else
                   Return (m_puntos(m_posicion))
            End If
      End Get
End Property

Ahora que hemos definido el enumerador para la clase, podemos implementar el método GetEnumerador en la clase CListaPuntoOrdenado.

Añadir el interfaz  IEnumerable


1. Hacer click en la interfaz IEnumerable en el nombre de método GetEnumerator. Y añadir esta declaración para el método.

Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return (New CEnumeradorPuntos(m_puntos))
End Function

Testear los interfaces


1. Modificar el código del botón del formulario para usar una estructura  For Each.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim puntos As New CListaPuntoOrdenado()
puntos.AddRandomPoints(250, 200)
Dim graficos As Graphics = Me.CreateGraphics
Dim contador As Integer = 1
Dim aColor As Color
Dim pt As CPuntoOrdenable

For Each pt In puntos
aColor = System.Drawing.Color.FromArgb(25, 25, contador)
            contador += 1
            Dim brocha As New System.Drawing.SolidBrush(aColor)
            graficos.FillEllipse(brocha, pt.X, pt.Y, 10, 10)
            brocha.Dispose()
Next
Dim enumerator As IEnumerator = puntos.GetEnumerator()
While enumerator.MoveNext
pt = CType(enumerator.Current, CPuntoOrdenable)
End While
End Sub

2. Presionar  F5 para ejecutar el programa, el resultado es similar al ejemplo inicial.

Implementando el interface IFormattable


Un posible formateo de cadena para mostrar un valor System.Decimal como valor de la moneda puede ser el siguiente:

MessageBox.Show (String.Format ("{0: C}", cuenta.saldo))

El tipo de datos
System.Decimal ha definido la cadena de formato C para que devuelva una cadena representando la moneda. También se pueden definir los esquemas de formato personalizados para las clases que nosotros creemos. En la tarea siguiente, vamos a añadir dos opciones de formato personalizado a la clase CPuntoOrdenado. Primero, se imprime una opción tipo long indicada por L,  imprime el punto como (x, y). En segundo lugar, una opción tipo Short indicada por S,  imprime el punto como x: y. Vamos a definir tres sobrecargas del método ToString para proporcionar un comportamiento de un formato coherente para la clase CPuntoOrdenado. Las tres sobrecargas se describen en la siguiente tabla.


Sobrecarga

Comportamiento

ToString()
Dim p As New CPuntoOrdenado(1,2)
Dim s As String = p.ToString()

Valor de s: (1,2)
ToString(format As String)
Dim p As New CPuntoOrdenado (1,2)
Dim plong As String = p.ToString("L")
Dim pshort As String = p.ToString("S")

Valor de plong:  (1,2)

Valor de pshort: 1;2
ToString(format As String, _
formatprovider As _
IFormatProvider)
Dim p As New CPuntoOrdenado (1,2)
Dim s As String =_
String.Format("{0:L}", p))

Valor de s; (1,2)



La primera sobrecarga reemplaza el método ToString que se encuentra en la clase System.Object. System.Object es la clase base para todos los tipos de Visual Basic. La segunda sobrecarga es un método ToString definido sólo para la clase. No es ni una sobrecarga  de una implementación de una clase base ni la  implementación de un método del  interfaz. La última sobrecarga es la  implementación del método IFormattable.ToString, el único miembro de la Interfaz IFormattable. Esta es la versión de ToString que se llama si es evaluado el formato de la expresión.
También se puede implementar una clase IFormatProvider para proporcionar opciones de formato adicionales para los tipos base. En este ejemplo, si este código no reconoce el formato  string, Con lo que hay que pasar a lo largo de la interfaz IFormatProvider a otra llamada con el método ToString.

Agregar el interfaz IFormattable


1. Añadir el interfaz IFormattable  a la clase CPuntoOrdenable:

Public Class CPuntoOrdenable
    Implements IComparable, IFormattable
End Class

2. Añadir este código al método IFormattable.ToString.

Debido a que la clase System.Object implementa menos parámetros en el método ToString, este método ToString es una sobrecarga. Si la clase CPuntoOrdenable no reconoce el formato string, los métodos individuales crean una cadena al pasar el formatProvider a los  métodos ToString de los campos individuales.

Public Function ToString1(ByVal formato As String, ByVal ProveedorFormato As System.IFormatProvider) As String Implements System.IFormattable.ToString

Dim resultado As String

Select Case formato.ToUpper()
Case ("L")
            resultado = String.Format("({0}, {1})", m_x, m_y)
      Case ("S")
            resultado = String.Format("{0},{1}", m_x, m_y)
      Case Else
resultado = (m_x.ToString(formato, ProveedorFormato) & " "     & m_y.ToString(formato, ProveedorFormato))
End Select
Return (resultado)

End Function

Sobrecargar el método ToString


Añadir dos sobrecargas de ToString más para que la clase  CPuntoOrdenable pueda
formatear de manera coherente en todos los métodos
ToString. Cada sobrecarga de llama al método ToString de la interfaz IFormattable. El método predeterminado ToString de la clase CPuntoOrdenable devuelve la versión larga.

Public Function ToString1(ByVal format As String) As String
Return Me.ToString1(format, Nothing)
End Function

Public Overrides Function ToString() As String
Return Me.ToString1("L")
End Function
   

Testear el interface IFormattable



1. Reemplazar el código en el controlador de eventos Click en el botón Dibujar con
este código, esto crea algunos puntos y luego los arrastra la etiqueta con sus coordenadas:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim puntos As New CListaPuntoOrdenado()
puntos.AddRandomPoints(5, 200)
Dim grafico As Graphics = Me.CreateGraphics
Dim pt As CPuntoOrdenable

For Each pt In puntos
grafico.FillEllipse(System.Drawing.Brushes.Black, pt.X,  pt.Y, 10, 10)
            Dim ptlocalizacion As String = String.Format("{0:L}", pt)
grafico.DrawString(ptlocalizacion, New Font("Arial",    8), System.Drawing.Brushes.Black, pt.X + 11, pt.Y)
        Next
End Sub

2. Presionar F5 para ejecutar el programa y ver los resultados

 
interfaces genéricas de .NET
Otros dos usos de los interfaces


En la programación con. NET Framework, se implementan interfaces principalmente para
prestación de servicios de nuestra clase. Es posible, además, utilizar una interfaz para un proyecto en las dos situaciones siguientes:
Herencia Múltiple clases  que sólo pueden tener una clase base, pero se pueden implementar varias interfaces. Debido a que las interfaces se comportan polimórficamente, como las clases  base, se pueden utilizar las interfaces para simular herencia múltiple. Supongamos que se crea una clase de
CPatio y de ella que se derivan  tanto CCesped y CJardin, pero no comparten una clase base común de System.Object. Podemos optar por implementar una interfaz ICesped y luego implementar la interfaz ICesped en una clase CCesped. A continuación, crear  CJardin como una clase base. Cuando se crea la clase de CPatio  esta hereda de CJardin e implementa ICesped como se puede ver aquí:

Public Class CPatio
    Inherits CJardin
    Implements ICesped
End Class

Al parecer, no se ha ganado mucho con este código ya que hay que reimplementar todos los miembros de ICesped. Afortunadamente, podemos servirnos de la contención y de la delegación para reutilizar parte de nuestro trabajo. En la clase CPatio, se puede crear una instancia privada  de la clase ICesped. Esta es la contención.
A continuación, implementaremos los métodos de ICesped mediante una llamada al método correspondiente de la instancia privada Cesped. Esto se llama delegación. Estamos delegando  el trabajo de la interfaz de ICesped  en el miembro contenido en la figura del miembro Cesped. Supongamos que la clase ICesped  tiene un método Crecer()  y una propiedad  Altura. Su código de podría ser algo como esto:
Public Class CPatio
    Inherits CJardin
    Implements ICesped
    Private m_cesped As New Cesped()
    Public Sub Crecer() Implements ICesped.Crecer
        m_cesped.Crecer()
    End Sub
    Public Property Altura() As Integer Implements ICesped.Altura
        Get
            Return m_cesped.Altura
        End Get
        Set(ByVal Value As Integer)
            m_cesped.Altura = Value
        End Set
    End Property
End Class


Anteriormente hemos creado un array de clases SourceFile para utilizar como fuente de datos en un control DataGrid. En general, se desea que el modelo de datos se adapte a los datos y, a continuación, se desea proporcionar métodos para apoyar a la interfaz de usuario. También podemos limitar la cantidad de control que la interfaz de usuario tiene sobre el modelo. Por  ejemplo, puede que no se desee  permitir actualizaciones. Se pueden evitar las actualizaciones al  implementar una interfaz en su clase. Cuando se crea una referencia al modelo de datos  para la interfaz de usuario, se proporciona sólo una referencia a la interfaz.  Por supuesto, se podría proporcionar una referencia al modelo completo, pero el uso de una interfaz puede dejar al compilador algunos de los trabajos, alertando  cuando se están tratando de actualizar los datos.

No hay comentarios:

Publicar un comentario