Calculadora Números Complejos (C# y WPF)

Calculadora Números Complejos (C# y WPF)

Java y C# son lenguajes muy parecidos, de hecho C# deriva de C++, Java y otros lenguajes alimentándose de lo bueno de cada uno; particularmente para mi es el mejor por su potencia y sus increíbles mejoras que nos hacen la programación mucho más fácil.

En el anterior post, describí una calculadora de números complejos en Java y en este, lo implemento con C# y WPF. Con esto intento tres cosas, en primer lugar demostrar que la clase (muy pero que muy similar a la de Java) es invariable porque me daría igual usar una aplicación de consola o gráfica, otra es que podáis ver la diferencia entre lenguajes pero la similitud estructural y por último implementarla mediante WPF con el cual nos vamos a ahorrar mucho código quedando la aplicación muy limpia y robusta y de camino os enseño como se aplican estilos a los controles.

Lo primero vayamos a la clase Complex. Las principales diferencias con la de Java son las que voy a enumerar.

  1. Implemento la interfaz INotifyPropertyChanged de modo que la clase debe contener el evento public event PropertyChangedEventHandler PropertyChanged; el cual invocamos mediante el método OnPropertyChanged(string p_PropertyName) cada vez que cambia la propiedad de la parte real o imaginaria de un número complejo.
  2. En esta clase, he sobrecargado las operaciones básicas de suma, resta, multiplicación, división y negación (a este operador le he asignado el opuesto). Para ver como lo he hecho, os paso el código de la operación suma con el que con un método
            public static Complex operator + (Complex _complex1, Complex _complex2)
            {
                return new Complex(_complex1.Real + _complex2.Real, _complex1.Imaginary + _complex2.Imaginary);
            }
    

    permitiéndome efectuar sumas sobre dos complejos.

A continuación paso el código completo de la clase.

/*************************************************************************************************************************************************
* © JOAQUIN MARTINEZ RUS 2015
* PROYECTO:        ComplexCalc. Calculadora de números complejos
* Archivo:         Complex.cs
* Descripción:     Clase de números complejos
* Historial:
*                  1. Joaquin Martínez Rus - 18 jul 2016. Creación
*
* Comentarios:
*
*
**************************************************************************************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ComplexCal
{
    public class Complex: INotifyPropertyChanged
    {
        #region Constructor

        public Complex(double _real=0, double _imaginary=0)
        {
            this.Real = _real;
            this.Imaginary = _imaginary;
        }

        public Complex(Complex _complex)
        {
            this.Real = _complex.Real;
            this.Imaginary = _complex.Imaginary;
        }

        #endregion

        #region Propiedades

        private double real;
        private double imaginary;

        public event PropertyChangedEventHandler PropertyChanged;

        public double Module { get; set; }
        public double DegreesAngle { get; set; }
        public double RadiansAngle { get; set; }

        public double Imaginary
        {
            get { return imaginary; }
            set
            {
                imaginary = value;
                SetPolarComplex();
                OnPropertyChanged("Imaginary");
                OnPropertyChanged("ToString");
            }
        }

        public double Real
        {
            get { return real; }
            set
            {
                real = value;
                SetPolarComplex();
                OnPropertyChanged("Real");
                OnPropertyChanged("ToString");
            }
        }

        public string ToBinomialString
        {
            get
            {
                return "z = " + this.Real.ToString("F2") + (this.Imaginary < 0 ? " - " : " + ") + Math.Abs(this.Imaginary).ToString("F2") + " i";
            }
        }

        public string ToPolarString
        {
            get
            {
                return "z = " + this.Module.ToString("F2") + ") " + this.DegreesAngle.ToString("F1") + "°";
            }
        }

        public string ToString
        {
            get { return this.ToBinomialString + new string(' ',10) + this.ToPolarString; }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Asigna el módulo y el argumento de un número complejo
        /// </summary>
        void SetPolarComplex()
        {
            this.Module= Math.Sqrt(Math.Pow(this.Real, 2) + Math.Pow(this.Imaginary, 2));
            this.RadiansAngle= Math.Atan2(this.Imaginary, this.Real);
            this.DegreesAngle = this.RadiansAngle * 180 / Math.PI;
        }

        #endregion

        #region Static Public Methods

        // SOBRECARGA DE LOS OPERADORES BÁSICOS

        public static Complex operator + (Complex _complex1, Complex _complex2)
        {
            return new Complex(_complex1.Real + _complex2.Real, _complex1.Imaginary + _complex2.Imaginary);
        }

        public static Complex operator - (Complex _complex1, Complex _complex2)
        {
            return new Complex(_complex1.Real - _complex2.Real, _complex1.Imaginary - _complex2.Imaginary);
        }

        public static Complex operator * (Complex _complex1, Complex _complex2)
        {
            return new Complex((_complex1.Real * _complex2.Real - _complex1.Imaginary * _complex2.Imaginary),
                (_complex1.Real * _complex2.Imaginary + _complex1.Imaginary * _complex2.Real));
        }

        public static Complex operator / (Complex _complex1, Complex _complex2)
        {
            double xx = (_complex1.Real * _complex2.Real + _complex1.Imaginary * _complex2.Imaginary) / (Math.Pow(_complex2.Real, 2) + Math.Pow(_complex2.Imaginary, 2));
            double yy = (_complex1.Imaginary * _complex2.Real - _complex1.Real * _complex2.Imaginary) / (Math.Pow(_complex2.Real, 2) + Math.Pow(_complex2.Imaginary, 2));
            return new Complex(xx, yy);
        }

        public static Complex operator ! (Complex _complex)
        {
            return new Complex(-_complex.Real, -_complex.Imaginary);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Inicia el evento de cambio de propiedad
        /// </summary>
        /// <param name="p_PropertyName">Nombre de la propiedad</param>
        public void OnPropertyChanged(string p_PropertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
            }
        }

        /// <summary>
        /// Efectúa una copia del objeto
        /// </summary>
        /// <param name="_complex">Objeto desde el cual se efectúa la copia</param>
        public void Clone(Complex _complex)
        {
            this.Real = _complex.Real;
            this.Imaginary = _complex.Imaginary;
        }

        /// <summary>
        /// Devuelve el conjugado de un número complejo
        /// </summary>
        /// <param name="complex">Número complejo al que se le calcula</param>
        /// <returns>Objeto Complex</returns>
        public Complex Conjugate(Complex complex)
        {
            return new Complex(complex.Real, -complex.Imaginary);
        }

        /// <summary>
        /// Devuelve el conjugado de un número complejo
        /// </summary>
        /// <returns>Objeto Complex</returns>
        public Complex Conjugate()
        {
            return Conjugate(this);
        }

        /// <summary>
        /// Determina cuando una instancia y un objeto tienen los mismos valores
        /// </summary>
        /// <param name="_complex">El objeto a comparar con la instancia</param>
        /// <returns>Valor booleano</returns>
        public bool Equals(Complex _complex)
        {
            if (_complex == null)
            {
                return false;
            }
            if (this.GetType() != _complex.GetType())
            {
                return false;
            }

            return this.Real == _complex.Real && this.Imaginary == _complex.Imaginary;
        }

        /// <summary>
        /// Obtiene el inverso del objeto Complex actual
        /// </summary>
        /// <returns>Objeto Complex</returns>
        public Complex Reverse()
        {
            return Reverse(this);
        }

        /// <summary>
        /// Obtiene el inverso de un objeto Complex
        /// </summary>
        /// <param name="_complex">Objeto Complex del que se obtiene el inverso</param>
        /// <returns>Objeto Complex</returns>
        public Complex Reverse(Complex _complex)
        {
            double denominador = Math.Pow(_complex.Real, 2) + Math.Pow(_complex.Imaginary, 2);
            return new Complex(_complex.Real / denominador, -_complex.Imaginary / denominador);
        }

        /// <summary>
        /// Retorna el objeto Complex en formato Polar
        /// </summary>
        /// <param name="isDegrees">La salida se muestra en grados sexagesimales</param>
        /// <returns>String</returns>
        public string PolarToString(bool isDegrees)
        {
            return this.Module.ToString("D1") + ")" + (isDegrees?this.DegreesAngle.ToString("D1"):this.RadiansAngle.ToString("D2"));
        }

        /// <summary>
        /// Retorna el objeto Complex en formato Polar
        /// </summary>
        /// <returns>String</returns>
        public string PolarToString()
        {
            return PolarToString(true);
        }

        #endregion
    }
}


Ahora damos paso al código XAML del formulario. Lo primero declaramos los recursos de la ventana con &lt;code&gt;Window.Resources&lt;/code&gt; y en este lugar, incluimos los estilos. He creado tres estilos, uno llamado &lt;span style='font-family: &quot;;&quot;;' data-mce-style='font-family: &quot;;'&gt;normal&lt;/span&gt; aplicado a los controles &lt;code&gt;TextBlock&lt;/code&gt;, otro llamado &lt;code&gt;header&lt;/code&gt; que hereda de normal y por tanto es aplicado a los mismos controles y un tercer estilo llamado &lt;code&gt;textBox&lt;/code&gt; aplicado a controles &lt;code&gt;TextBox&lt;/code&gt; (muy original, eh?), por tanto a cada control que le asigne este estilo se le aplicarán los valores de las propiedades incluidas en el estilo a no ser que se sobreescriban al crear el control.( En el control &lt;code&gt;TextBox&lt;/code&gt; he incluido un validador para que no se incluya texto que no sea numérico, pero eso lo veremos otro día)Una vez creados los estilos vamos a centrarnos en&amp;nbsp;el control &lt;code&gt;TextBlock&lt;/code&gt; del siguiente código: 
 
      <StackPanel x:Name="stackResult" Orientation="Horizontal" Grid.Column="2" Grid.Row="5" Grid.ColumnSpan="5">
            <TextBlock x:Name="result"  Style="{StaticResource header}" Text="Resultado" VerticalAlignment="Center"/>
            <TextBlock x:Name="labelComplexNumberResult" Style="{StaticResource normal}" Text="{Binding ToString}" Margin="123,0,0,0"/>
</StackPanel>  
  1. Le asignamos un nombre
  2. Le asignamos el estilo con Style="{StaticResource textBox}"
  3. Le asignamos el valor enlazado de la propiedad Text con  la propiedad Real de la clase Complex. Style="{StaticResource textBox}" Text="{Binding Real}"¿Y de donde extrae los datos la propiedad Text?

Para que los controles sepan de donde tienen que extraer los datos de la clase Complex, al crear la ventana principal, asigno a cada StackPanel donde se encuentran contenidos los controles de cada número, la clase desde donde se alimentará y cada vez que se alteren los datos de cada clase, se visualizarán automáticamente en los controles sin necesidad de código. Por tanto en el code-behind de la ventana principal solo y exclusivamente incluyo la declaración de los tres objetos Complex, los dos de cálculo y el resultado, la asignación del DataContext de los StackPanel, los métodos de los eventos de cambio de propiedad y cambio de operación y el cálculo  de la operación.

/* ************************************************************************************************************************************************
* © JOAQUIN MARTINEZ RUS 2015
* PROYECTO:        ComplexCalc. Calculadora de números complejos
* Archivo:         MainWindow.cs
* Descripción:     Clase de la ventana principal
* Historial:
*                  1. Joaquin Martínez Rus - 18 jul 2016. Creación
*
* Comentarios:
*
*
**************************************************************************************************************************************************/
using System.Windows;
using System.Windows.Controls;

namespace ComplexCal
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Complex c1 = new Complex(2,4);
        Complex c2 = new Complex(3,5);
        Complex ComplexResult=new Complex();

        public MainWindow()
        {
            InitializeComponent();

            // asignar a los Stackpanel el Datacontext de cada objeto Complex

            stackC1.DataContext = c1;
            stackC2.DataContext = c2;
            stackResult.DataContext = ComplexResult;

            // Crear evento para que cada vez que se cambie una propiedad, se calcule la operación
            c1.PropertyChanged += Complex_PropertyChanged;
            c2.PropertyChanged += Complex_PropertyChanged;

        }

        private void Complex_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Calculate(comboBox.SelectionBoxItem.ToString());
        }

        private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ComboBoxItem op = e.AddedItems[0] as ComboBoxItem;
            Calculate(op.Content.ToString());
        }

        /// <summary>
        /// Efectúa el cálculo entre dos complejos
        /// </summary>
        /// <param name="operatorToString">Operador</param>
        void Calculate(string operatorToString)
        {
            switch (operatorToString)
            {
                case "+":
                    ComplexResult.Clone(c1 + c2);
                    break;
                case "-":
                    ComplexResult.Clone(c1 - c2);
                    break;
                case "x":
                    ComplexResult.Clone(c1 * c2);
                    break;
                case "÷":
                    ComplexResult.Clone(c1 / c2);
                    break;
                case "Inverso":
                    ComplexResult.Clone(c1.Reverse());
                    break;
                case "Conjugado":
                    ComplexResult.Clone(c1.Conjugate());
                    break;
                case "Opuesto":
                    ComplexResult.Clone(!c1);
                    break;
            }

        }

    }
}

y el código XAML full:

<Window x:Class="ComplexCal.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ComplexCal"
        mc:Ignorable="d"
        Title="Complex Calc" Height="480" Width="640">
    <Window.Resources>

        <Style x:Key="normal" TargetType="TextBlock">
            <Setter Property="VerticalAlignment" Value="Center"/>            
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="FontFamily" Value="Segoe UI"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="Width" Value="Auto"/>
        </Style>
        <Style x:Key="header" TargetType="TextBlock" BasedOn="{StaticResource normal}">
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="VerticalAlignment" Value="Bottom"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>            
        </Style>
        <Style x:Key="textBox" TargetType="TextBox">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="Width" Value="50"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="true">
                            <Border Background="OrangeRed" DockPanel.Dock="right" Margin="5,0,0,0" 
                                Width="20" Height="20" CornerRadius="5"
                                ToolTip="{Binding ElementName=customAdorner, 
                                          Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                                <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" 
                                   FontWeight="Bold" Foreground="white" />
                            </Border>
                            <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                                <Border BorderBrush="red" BorderThickness="1" />
                            </AdornedElementPlaceholder>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Margin" Value="20,0,0,0"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="28*"/>
            <ColumnDefinition Width="27*"/>
            <ColumnDefinition Width="82*"/>
            <ColumnDefinition Width="132*"/>
            <ColumnDefinition Width="154*"/>
            <ColumnDefinition Width="60*"/>
            <ColumnDefinition Width="149*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="49*"/>
            <RowDefinition Height="155*"/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="labelReal" Grid.Column="2" Grid.Row="1" Style="{StaticResource header}" Text="Real"/>
        <TextBlock x:Name="labelImaginaria" Grid.Column="3" Grid.Row="1" Style="{StaticResource header}" Text="Imaginaria"/>
        <TextBlock x:Name="labelNumberHeader" Grid.Column="4" Grid.Row="1" Style="{StaticResource header}" Text="Número complejo"/>
        <TextBlock x:Name="z1" Grid.Column="1" Grid.Row="2" Style="{StaticResource normal}" Text="z1" Margin="0,12"/>
        <TextBlock x:Name="z2" Grid.Column="1" Grid.Row="4" Style="{StaticResource normal}" Text="z2" Margin="0,12" />
        <TextBlock x:Name="op" Grid.Column="2" Grid.Row="3" Style="{StaticResource normal}" Text="Operación" Margin="0,12"/>
        <ComboBox x:Name="comboBox" Grid.Column="3" HorizontalAlignment="Left" Grid.Row="3" VerticalAlignment="Center" Width="80" Height="25" SelectionChanged="comboBox_SelectionChanged">
            <ComboBoxItem Content="+" IsSelected="True"/>
            <ComboBoxItem Content="-"/>
            <ComboBoxItem Content="x"/>
            <ComboBoxItem Content="÷"/>
            <ComboBoxItem Content="Inverso"/>
            <ComboBoxItem Content="Conjugado"/>
            <ComboBoxItem Content="Opuesto"/>
        </ComboBox>
        <StackPanel x:Name="stackC1" Orientation="Horizontal" Grid.Column="2" Grid.Row="2" Grid.ColumnSpan="5">
            <TextBox x:Name="textBoxReal1"  Style="{StaticResource textBox}" Text="{Binding Real}"/>
            <TextBox x:Name="textBoxImagin1" Style="{StaticResource textBox}" Text="{Binding Imaginary}"/>
            <TextBlock x:Name="labelComplexNumber1" Style="{StaticResource normal}" Text="{Binding ToString}" Margin="50,0,0,0" />
        </StackPanel>
        <StackPanel x:Name="stackC2" Orientation="Horizontal" Grid.Column="2" Grid.Row="4" Grid.ColumnSpan="5">
            <TextBox x:Name="textBoxReal2" Style="{StaticResource textBox}" Text="{Binding Real}"/>
            <TextBox x:Name="textBoxImagin2" Style="{StaticResource textBox}" Text="{Binding Imaginary}"/>
            <TextBlock x:Name="labelComplexNumber2" Style="{StaticResource normal}" Text="{Binding ToString}" Margin="50,0,0,0" />
        </StackPanel>
        <StackPanel x:Name="stackResult" Orientation="Horizontal" Grid.Column="2" Grid.Row="5" Grid.ColumnSpan="5">
            <TextBlock x:Name="result"  Style="{StaticResource header}" Text="Resultado" VerticalAlignment="Center"/>
            <TextBlock x:Name="labelComplexNumberResult" Style="{StaticResource normal}" Text="{Binding ToString}" Margin="123,0,0,0"/>
        </StackPanel>
    </Grid>
</Window>


Pues esto es todo, la clase Complex en C# y su ejecución bajo WPF con los controles enlazados.

Nos vemos. Saludos

Anuncio publicitario

Calculadora de números complejos en Java

Calculadora de números complejos en Java

En este caso, voy a detallar una clase sencilla que implementa una calculadora de números complejos en Java.

Para los que no lo recuerden, un número complejo consta de dos partes, una parte real y una parte imaginaria; la parte imaginaria se compone del número √-1 o lo que es lo mismo i. Un número complejo lo podemos representar vectorialmente como z = (a,b), binomial z = a + bi, polar mediante un módulo y un ángulo z = r α, trigonométrica z = r (cos α + i sin α) y por último la forma exponencial y que no voy a incluir en la clase aunque a mi parecer es la más elegante e = cos α + i sin α.

Cada forma tiene su fin, por ejemplo la suma y las resta son más fáciles con la forma binomial mientras que la multiplicación, división y exponenciación lo son con la forma polar; en el caso de la clase todos los cálculos los he realizado con la forma binomial.

Si pasamos a la clase, como ya he comentado está implementada en Java y consta de cuatro propiedades, parte real mediante x, parte imaginaria mediante y, el módulo que es calculado en base de x e y, y por último el argumento o ángulo.A continuación paso el diagrama de la clase donde se exponen los métodos con la mayoría de las operaciones:

Pasando un ejemplo

// Pasamos dos números complejos
        // Efectuamos los cálculos
        Complex c1=new Complex(4,5);
        Complex c2=new Complex(7,6);

        System.out.println("(" + c1.toString() + ") + (" + c2.toString() + ") = " + c1.addComplex(c2));
        System.out.println("(" +c1.toString() + ") - (" + c2.toString() + ") = " + c1.substractComplex(c2));
        System.out.println("(" +c1.toString() + ") x (" + c2.toString() + ") = " + c1.multiplyComplex(c2));
        System.out.println("(" +c1.toString() + ") / (" + c2.toString() + ") = " + c1.divideComplex(c2));
        System.out.println("Opuesto de " + c1.toString() + " = " + c1.opposite().toString());
        System.out.println("Inverso  de " + c1.toString() + " = " + c1.reverse().toString());
        System.out.println(c1.toString() + " = " + c1.polarToString());
        System.out.println(c1.toString() + " = " + c1.polarToString(true));
        System.out.println(c2.toString() + " = " + c2.polarToString());
        System.out.println(c2.toString() + " = " + c2.polarToString(true));
        c2.convertToBinomial(5, 36.86*Math.PI/180);
        System.out.println("Convertir 5) 36.86º a binomial = " + c2.toString());<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

Resultado

(4 + 5i) + (7 + 6i) = 11 + 11i
(4 + 5i) - (7 + 6i) = -3 -1i
(4 + 5i) x (7 + 6i) = -2 + 59i
(4 + 5i) / (7 + 6i) = 0,68 + 0,13i
Opuesto de 4 + 5i = -4 -5i
Inverso  de 4 + 5i = 0,1 -0,12i
4 + 5i = 5,7)0,896 radians
4 + 5i = 5,7)51,3º
7 + 6i = 9,9)0,709 radians
7 + 6i = 9,9)40,6º
Convertir 5) 36.86º a binomial = 4 -3i

A continuación paso el código:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package complex;

/******************************************************
 *  @author:    Joaquín Martínez Rus (c) 2016
 *  @version:   1.0 
 *  File:       Estrella.java
 *  Created:    17/07/2016
 *  Project:    Calculadora de números complejos
 *  Comments:   Clase Complex.
 *******************************************************/
public class Complex {
    
    /**
     * Inicia una nueva instancia de la clase Complex con valor z = 0 + 0i;
     */
    public Complex(){
        this(0,0);
    }
    
    /**
     * Inicia una nueva instancia de la clase Complex
     * @param _x Parte real
     * @param _y Parte imaginaria
     */
    public Complex(double _x, double _y){
        this.x=_x;
        this.y=_y;
        this.setModule();
        this.setGrades();
    }
    
    double x;
    double y;
    double module;
    double grades;

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getModule() {
        
        return module;
    }

    /**
     * Asigna y calcula el valor del módulo
     */
    public void setModule(){
        this.module=Math.sqrt(Math.pow(x, 2)+ Math.pow(x, 2));
    }
    
    /**
     * Asigna el valor del módulo
     */
    public void setModule(double module) {
        this.module = module;
    }

    public double getGrades() {
        return grades;
    }

    public void setGrades() {
        this.grades = Math.atan2(y, x);
    }
    
    public void setGrades(double grades) {
        this.grades = grades;
    }
    
    /**
     * Suma al número complejo otro número complejo
     * @param _complex Número complejo de la suma
     * @return Objeto Complex
     */
    public Complex addComplex(Complex _complex){
        return new Complex(this.x + _complex.x, this.y + _complex.y);
    }
    
    /**
     * Resta al número complejo otro número complejo
     * @param _complex Número complejo de la resta
     * @return Objeto Complex
     */
    public Complex substractComplex(Complex _complex){
        return new Complex(this.x - _complex.x, this.y - _complex.y);
    }
    
    /**
     * Multiplica al número complejo otro número complejo
     * @param _complex Número complejo de la multiplicación
     * @return Objeto Complex
     */
    public Complex multiplyComplex(Complex _complex){
        return new Complex((this.x * _complex.x - this.y * _complex.y), 
                (this.x * _complex.y + this.y * _complex.x));
    }
    
    /**
     * Divide el número complejo actual entre el valor del parámetro
     * @param _complex Denominador
     * @return Objeto Complex
     */
    public Complex divideComplex(Complex _complex){
        return divideComplex(this,_complex);
    }
    
    /**
     * Divide dos números complejos
     * @param _complex1 Numerador
     * @param _complex2 Denominado
     * @return Objeto Complex
     */
    public Complex divideComplex(Complex _complex1, Complex _complex2){
        double xx = (_complex1.x * _complex2.x + _complex1.y * _complex2.y)/(Math.pow(_complex2.x,2)+Math.pow(_complex2.y,2));
        double yy = (_complex1.y * _complex2.x - _complex1.x * _complex2.y)/(Math.pow(_complex2.x,2)+Math.pow(_complex2.y,2));
        return new Complex(xx, yy);
    }
    
    /**
     * Obtiene un número complejo en forma vectorial
     * @return Cadena de texto
     */
    public String vectorialtoString(){
        return "(" + ConsoleTools.toNumber(this.x,2) + ", " + ConsoleTools.toNumber(this.y,2) + ")";
    }
    
    /**
     * Obtiene un número complejo en forma binomial
     * @return Cadena de texto
     */
    @Override
    public String toString(){
        return ConsoleTools.toNumber(this.x,2) + (this.y < 0? "": " + ") + ConsoleTools.toNumber(this.y,2) + "i";
    }
    
    /**
     * Obtiene un número complejo en forma polar
     * @return Cadena de texto
     */
    public String polarToString(){
        return  polarToString(false);
    }
    
    /**
     * Obtiene un número complejo en forma polar
     * @param isDegrees Mostrar como grados centigrados o radianes
     * @return Cadena de texto
     */
    public String polarToString(boolean isDegrees){
        double angle=isDegrees?this.getGrades()*180/Math.PI:this.getGrades();
        String angleToString = (isDegrees?ConsoleTools.toNumber(angle,1):ConsoleTools.toNumber(angle,3)) + (isDegrees? "º": " radians");
        return  ConsoleTools.toNumber(this.module,1) + ")" + angleToString;
    }
    
    /**
     * Obtiene el conjugado de un número complejo
     * @param complex Número complejo
     * @return Objeto Complex
     */
    public Complex conjugate(Complex complex){
        return new Complex(complex.x, - complex.y);
    }
    
    /**
     * Calcula el opuesto de un número complejo
     * @return Objeto Complex
     */
    public Complex opposite(){
        return opposite(this);
    }
    
    /**
     * Calcula el opuesto de un número complejo
     * @param _complex Número complejo
     * @return Objeto Complex
     */
    public Complex opposite(Complex _complex){
        return new Complex(-_complex.x, - _complex.y);
    }
    /**
     * Obtiene el conjugado de un número complejo
     * @return Objeto Complex
     */
    public Complex conjugate(){
        return conjugate(this);
    }
    
    /**
     * Obtiene el inverso de un número complejo
     * @return Objeto Complex
     */
    public Complex reverse(){
        return reverse(this);
    }
    
    /**
     * Obtiene el inverso de un número complejo
     * @param complex Número complejo del cálculo
     * @return Objeto Complex
     */
    public Complex reverse(Complex complex){
        double denominador=Math.pow(complex.x, 2) + Math.pow(complex.y, 2);  
        return new Complex( complex.x/denominador, - complex.y/denominador);
    }
    
    /**
     * Convierte un número complejo de forma polar a binomial
     * @param module Modulo del número complejo
     * @param argument Argumento en radianes del número complejo
     * @return Objeto Complex
     */
    public Complex convertPolarToBinomial(double module, double argument){
        double _x = Math.abs(module) * Math.cos(argument);
        double _y = Math.abs(module) * Math.sin(argument);
        return new Complex(_x,-_y);
    } 
    
    /**
     * Asigna los valores real e imaginario en base al módulo y el argumento
     * @param module Modulo del número complejo
     * @param argument Argumento en radianes del número complejo
     */
    public void convertToBinomial(double module, double argument){
        Complex _complex=convertPolarToBinomial(module, argument);
        this.x=_complex.x;
        this.y=_complex.y;
    }
    
    /**
     * Comprueba dos números complejos
     * @param _complex Número complejo a comparar
     * @return Objeto Complex
     */
    public boolean equals(Complex _complex){
        if (_complex==null) {
            return false;
        }
        if (this.getClass()!=_complex.getClass()) {
            return false;
        }
        
        return this.x ==_complex.x && this.y == _complex.y;
    }
}


Consejos.

  • La clase debe estar bien definida. Constructores, propiedades y métodos
  • Antes de escribir código, genera un diagrama de clases como mínimo (esto implica pensar que vas a hacer), además una buena estructura puede hacer nuestro código más robusto.
  • Piensa en las cuatro características de la Programación Orientada a Objetos, Abstracción, Encapsulamiento, Herencia y Polimorfismo (hay alguna más, pero estas son las principales y debes tenerlas muy claras)
  • Los métodos deben contener el código justo. Un método con mucho código no hace la clase legible y lo vuelve débil.
  • Las clases deben funcionar en cualquier medio. Si usara esta clase en modo gráfico con ventanas en vez de modo consola, debería de funcionar del igual modo, solo debo llamar al método de operación y obtener su resultado mediante los métodos apropiados.
  • Usa los comentarios, tanto dentro de los métodos como en su documentación. Por ejemplo, si llamo al método divideComplex de la clase Complex, cuando estoy escribiendo el método, aparecerá el método y el texto que nosotros escribimos, en este caso yo escribí «Divide el número complejo actual entre el valor del parámetro» junto con los parámetros y el valor retornado. Cuando las clases se hacen muy grandes y complejas, es necesario documentarlas todo lo que se pueda.
  • Además de los comentarios, a mi me gusta agrupar el código por regiones con Java uso
    // 

    /**
     * Devuelve un valor desde el teclado
     * @param textIn Texto que se visualiza en la consola
     * @return Texto procedente del teclado
     * @throws IOException
     */
    public static String readText(String textIn, boolean allowEmpty) throws IOException{

        // Declaración de variables
        BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
        String textOut=null;

        // Imprimir texto de consola
        System.out.println(textIn);

        // Solicitar datos desde teclado

        do {
            textOut=keyboardIn.readLine();
        } while (ConsoleUtilities.isNullOrEmpty(textOut) && !allowEmpty);

        // Retornar texto
        return textOut;
    }

    // 

Esto me va permitir expandir o contraer todo el código contenido entre las etiquetas o para el caso de C#

#region Private Methods
      // Aquí iría el código en C#
#endregion

o Visual Basic

#Región MiRegion
     // Aquí iría el código en Visual Basic
#End Region
  • Cíñete a la nomenclatura estándar con cada lenguaje de programación, por ejemplo Java o C# o Visual Basic.
  • Créate un clase con herramientas, por ejemplo, para aplicaciones del tipo consola en Java, tengo una clase con métodos estáticos donde implemento herramientas que puedo usar en este medio como un método que genera un menú automáticamente, un lector desde teclado de texto o números, un formateador de texto, un serializador, etc. Para C# tengo otro tipo de clases donde extiendo funcionalidades a las clases creadas, en fin, código que reutilizaré más a menudo de lo que me pienso.
  • Si hacemos todo esto, en un futuro nos será más fácil, modificar, entender que hicimos o ampliar nuestras clases.
Y esto es todo por hoy. Saludos!

Mensajería única (2)

Una vez que podemos pasar datos entre formularios como en la entrada Mensajería únicaWPF nos lo pone mucho más fácil y con mucho menos código.

Lo primero de todo debemos enlazar los controles con los datos de la clase y para ello debemos tener algún conocimiento de XAML y WPF (no es el fin de este artículo enseñar WPF)
Para llevar a cabo el paso de mensajes entre formularios mediante XAML, he creado una nueva ventana con un control Grid con tres columnas y tres filas, un control TextBlock y un control ProgressBar.
Las propiedades de los controles se enlazan con datos, estos datos proceden de una clase de negocio y esta clase es Message.

En WPF, para instanciar una clase, debemos hacerlo desde los recursos de la página o de un control, dependiendo del ámbito que se pretenda abarcar. En este caso, yo declaro la clase Message en los recursos del objeto Grid.
Una vez declarada, se creará una instancia de Message en cada nueva ventana y los controles se enlazan a la clase con Bindng en la propiedad del control que pretendemos enlazar y haciendo referencia a la propiedad de la clase Message. He incluido un conversor en la propiedad Visibility del control ProgressBar para convertir valores booleanos en valores de la enumeración Visibility como Visible, Hidden o Collapse; si es true, Visible y si es False, Collapse. En este caso hacemos los mismo, que es declarar la clase ConvertBoolToVisible (la cual implementa la interfaz IValueConverter) con una key llamada btv.

He hecho un binding sobre la propiedad Text del TextBlock a la propiedad infoText de la clase Message, la propiedad Value del control ProgressBar con la propiedad progress de Message y por último la propiedad Visibility con progressIsVisible ya su vez con el conversor btv.

<Window x:Class="WpfApplication1.Window2"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"         xmlns:local="clr-namespace:WpfApplication1"         mc:Ignorable="d"         Title="Window2" Height="300" Width="300">

    <Grid x:Name="grid" DataContext="message">
        <Grid.Resources>
            <local:ConvertBoolToVisible x:Key="btv"/>
            <local:Message x:Key="message"/>
        </Grid.Resources>

        <Grid.RowDefinitions>
            <RowDefinition Height="100"/>
            <RowDefinition Height="100*"/>
            <RowDefinition Height="100*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100*"/>
            <ColumnDefinition Width="100*"/>
            <ColumnDefinition Width="100*"/>
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="tb" Text="{Binding infoText}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <ProgressBar x:Name="pb" Minimum="0" Maximum="100" Value="{Binding progress}"                      Visibility="{Binding Path=progressIsVisible, Converter={StaticResource btv}}"                      Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" HorizontalAlignment="Center" Width="150" Margin="71,0,71,65"/>
    </Grid>
</Window>

Y el código (code-behind)? Muchísmo más reducido, ya que en este caso solo existe un objeto Message de clase, asignamos dinámicamente el DataContext del control Grid (que será desde donde se alimenten los controles) y añadimos el evento CollectionChanged.

Para este caso, he efectuado dos cambios en la clase Message:

  • Creación de un nuevo método público llamado Clone en la clase Message que copia las propiedades de un objeto Message al mismo objeto.
  • Implementación de la interfaz INotifyPropertyChanged por la clase Message de modo que debe implementar el método OnPropertyChanged para que la ventana detecte los cambios en las propiedades y pueda asignar los valores.
  • Creación del método OnPropertyChanged("propertyName") el cual llamará al evento this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
  • Modificación en el set de las propiedades, incluyendo la llamada al método OnPropertyChanged("propertyName").

Cada vez que se añada un nuevo mensaje, se clonará el objeto mensaje con los nuevos datos y se visualizarán en pantalla efectuando el mismo resultado que conseguimos mediante código, pero esta vez con menos código.

Clase Message modificada

using System;
using System.ComponentModel;

namespace WpfApplication1
{
    public class Message: INotifyPropertyChanged
    {
        #region Constructor

        public Message()
        {
            this.infoText = ""
            this.progress = 0;
            this.progressIsVisible = false;
        }

        public Message(string _text, bool _isVisible, int _progress)
        {
            setMessage(_text, _isVisible, _progress);
        }

        #endregion

        #region Properties

        private string _infotext;
        private int _progress;

        public int progress
        {
            get { return _progress; }
            set
            {
                _progress = value;

                OnPropertyChanged("progress");
            }
        }

        public string infoText
        {
            get { return _infotext; }
            set
            {
                _infotext = value;
                OnPropertyChanged("infoText");
            }
        }

        private bool _progressIsVisible;

        public bool progressIsVisible
        {
            get { return _progressIsVisible; }
            set
            {
                _progressIsVisible = value;
                OnPropertyChanged("progressIsVisible");
            }
        }

        public string infoText2 { get; set; }

        #endregion

        #region Events

        public event EventHandler<EventArgsMessage> ChangedTextEventHandler;
        public event EventHandler<EventArgsProgress> ChangedProgressEventHandler;
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(String p_PropertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
            }
        }

        #endregion

        #region Private Methods

        void OnChangedText(EventArgsMessage e)
        {
            if (ChangedTextEventHandler!=null)
            {
                ChangedTextEventHandler(this, e);
            }
        }

        void OnChangedProgress(EventArgsProgress e)
        {
            if (ChangedProgressEventHandler!=null)
            {
                ChangedProgressEventHandler(this, e);
            }
        }

        #endregion

        #region Public Methods

        ///
<summary>
        /// Asigna los valores al mensaje
        /// </summary>

        /// <param name="_text">Texto del mensaje
        /// <param name="_isVisible">Visibilidad del mensaje
        /// <param name="_progress">Progreso
        public void setMessage(string _text, bool _isVisible, int _progress)
        {
            this.infoText = _text;
            this.progressIsVisible = _isVisible;
            this.progress = _progress;
        }

        ///
<summary>
        /// Borra los valores del mensaje
        /// </summary>

        public void Clear()
        {
            setMessage("", false, 0);
        }

        ///
<summary>
        /// Efectua una copia de las propiedades de un mensaje
        /// </summary>

        /// <param name="message">Objeto a clonar
        public void Clone(Message message)
        {
            this.infoText = message.infoText;
            this.infoText2 = message.infoText2;
            this.progressIsVisible = message.progressIsVisible;
            this.progress = message.progress;
        }

        #endregion
    }

    public class EventArgsMessage:EventArgs
    {
        public EventArgsMessage() { }
        public EventArgsMessage (string _oldText, string _newText)
        {
            this.oldText = oldText;
            this.newText = _newText;
        }

        public string oldText { get; set; }
        public string newText { get; set; }
    }

    public class EventArgsProgress : EventArgs
    {
        public EventArgsProgress() { }
        public EventArgsProgress(int _newValue)
        {
            this.newValue = _newValue;
        }

        public int newValue { get; set; }
    }

}

Code-Behind de la ventana

public partial class Window2 : Window
    {
        public Window2()
        {
            InitializeComponent();
            App.messages.CollectionChanged += Messages_CollectionChanged;
            this.grid.DataContext = m;
        }

        Message m = new Message();

        private void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    m.Clone((Message)item);
                }
            }

        }
    }

He aquí una muestra

Pues esto es todo. Estoy a vuestra disposición para cualquier duda.

Un saludo

Mensajería única

Mensajería única
Estaba mejorando mi sistema de mensajería para mis aplicaciones y he pensado, ¿por qué no lo publicas?, pues ahí va. (Este en concreto es con WPF)
Cuando desarrollo mis aplicaciones, intento que todas tengan un sistema de mensajería con el que pueda comunicarse cualquier ventana de la aplicación con esta, de modo que generando un mensaje por ejemplo, «cargando caché de datos…» y una barra de progreso indique su estado, aparezca en la barra de estado o mediante un cuadro de dialogo, el mensaje «cargando caché de datos…» y la barra aumente su progreso, de modo que si tengo varias ventanas abiertas, podría visualizar los mismos mensajes en todas y esto mediante un ejemplo os lo voy a mostrar. Para comenzar creo una clase llamada Message que contiene cuatro propiedades, infoText que almacena el texto del mensaje principal, progress que almacena el valor del progreso, progressIsVisible que almacena si la barra de progreso es visible y por último infoText2 que almacena un segundo texto por si acaso lo necesito.
A esta clase le añado varios eventos, por si acaso los necesito, que son cuando cambia el texto del mensaje o cuando cambia el progreso. Extiendo varios EventArgs con EventArgsMessage para obtener los valores nuevo y antiguos y el valor del progreso EventArgsProgress. Contiene dos métodos, uno para asignar los valores de la propiedades y otro para borrarlos.
Por otra parte creo una colección del tipo ObservableCollection en la clase App de la aplicación de modo que cualquier ventana puede acceder a esta colección si la hacemos pública, la cual contiene un evento CollectionChanged el cual usaremos en cada ventana para capturar los mensajes.

public static ObservableCollection<Message>; messages = new ObservableCollection<Message>();

Y ahora solo nos queda capturar los eventos en cada ventana. Para ver esto, voy a crear una ventana principal que iniciará un hilo desde un botón con un proceso que suma desde 1 hasta 100 con un retardo de 50 ms y que en cada incremento, actualizará un control Textblock con el texto del mensaje y una barra de progreso con un valor. Al mismo tiempo, abriré otra ventana en la aplicación con un TextBlock y otra barra de progreso que deberían actualizarse de igual modo que lo hace la ventana principal y sin albergar ningún código en ella que le permita incrementar nada ni mostrar nada. Para mostrar el mensaje y actualizar las barras uso un delegado en cada ventana que se encargará de hacer este trabajo y para acceder a los controles sin errores, Dispatcher.Invoke(new MessageAddedHandler(setMessage), message) invocando al delegado y como argumento el nuevo mensaje. ¿Qué resultado obtendremos? Dos ventanas, la que contiene el botón y la que no. Al pulsar se inicia la carga y en ambas ventanas se actualizan tanto el texto como el valor de la barra de progreso al mismo tiempo.

Pasamos al código.

Clase Message

using System;
namespace WpfApplication1
{
    public class Message
    {
        #region Constructor
        public Message()
        {
            this.infoText = "";
            this.progress = 0;
            this.progressIsVisible = false;
        }
        public Message(string _text, bool _isVisible, int _progress)
        {
            setMessage(_text, _isVisible, _progress);
        }
        #endregion
        #region Properties
        private string _infotext;
        private int _progress;
        public int progress
        {
            get { return _progress; }
            set
            {
                if (_progress!=value)
                {
                }
                _progress = value;
            }
        }
        public string infoText
        {
            get { return _infotext; }
            set
            {
                if (_infotext!=value)
                {
                    EventArgsMessage eventArgsMessage = new EventArgsMessage(_infotext,value);
                }
                _infotext = value;
            }
        }
        public bool progressIsVisible { get; set; }
        public string infoText2 { get; set; }
        #endregion
        #region Events
        public event EventHandler<eventargsmessage> ChangedTextEventHandler;
        public event EventHandler<eventargsprogress> ChangedProgressEventHandler;
        #endregion
        #region Private Methods
        void OnChangedText(EventArgsMessage e)
        {
            if (ChangedTextEventHandler!=null)
            {
                ChangedTextEventHandler(this, e);
            }
        }
        void OnChangedProgress(EventArgsProgress e)
        {
            if (ChangedProgressEventHandler!=null)
            {
                ChangedProgressEventHandler(this, e);
            }
        }
        #endregion
        #region Public Methods
        ///
<summary>
        /// Asigna los valores al mensaje
        /// </summary>

        /// <param name="_text" />Texto del mensaje
        /// <param name="_isVisible" />Visibilidad del mensaje
        /// <param name="_progress" />Progreso
        public void setMessage(string _text, bool _isVisible, int _progress)
        {
            this.infoText = _text;
            this.progressIsVisible = _isVisible;
            this.progress = _progress;
        }
        ///
<summary>
        /// Borra los valores del mensaje
        /// </summary>

        public void Clear()
        {
            setMessage("", false, 0);
        }
        #endregion
    }
    public class EventArgsMessage:EventArgs
    {
        public EventArgsMessage() { }
        public EventArgsMessage (string _oldText, string _newText)
        {
            this.oldText = oldText;
            this.newText = _newText;
        }
        public string oldText { get; set; }
        public string newText { get; set; }
    }
    public class EventArgsProgress : EventArgs
    {
        public EventArgsProgress() { }
        public EventArgsProgress(int _newValue)
        {
            this.newValue = _newValue;
        }
        public int newValue { get; set; }
    }
}

Clase <code>App</code>

public partial class App : Application
    {
        public static ObservableCollection<message> messages = new ObservableCollection<message>();

        public App()
        {

        }
    }

Ventana principal con método de cálculo

Constructor

public MainWindow()
        {
            InitializeComponent();
            App.messages.CollectionChanged += Messages_CollectionChanged;
            Window1 w1 = new Window1();
            w1.Show();
        }

Propiedades. El delegado, un objeto CancellationTokenSource para cancelar el proceso y un flag para saber si la app está corriendo o no.

        delegate void MessageAddedHandler(Message _message);
        CancellationTokenSource cs;
        public bool isRunning { get; set; } = false;

Captura del evento. Ocurre cuando la colección cambia.

void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action==System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    Dispatcher.Invoke(new MessageAddedHandler(setMessage), (Message)item);
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                Dispatcher.Invoke(new MessageAddedHandler(setMessage), new Message());
            }

        }

Métodos. El primer método asigna un mensaje a los controles, el segundo inicia el proceso o lo cancela y el tercero suma 1 después de 50 ms: una vez que acaba, finaliza y borra los mensajes.

void setMessage(Message message)
        {
            this.textBlock.Text = message.infoText;
            this.pb.Value = message.progress;
            this.pb.Visibility = message.progressIsVisible ? Visibility.Visible : Visibility.Collapsed;
        }

         private void button_Click(object sender, RoutedEventArgs e)
        {
            if (isRunning)
            {
                this.button.Content = "Iniciar";
                cs.Cancel();
            }
            else
            {
                isRunning = true;
                cs = new CancellationTokenSource();
                this.button.Content = "Cancelar";
                var t = Task.Factory.StartNew(() => doSomeThing(cs.Token),cs.Token);

            }
        }
        void doSomeThing(CancellationToken ct)
        {
            try
            {
                for (int i = 0; i <= 100; i++)
                {
                    ct.ThrowIfCancellationRequested();
                    Thread.Sleep(50);
                    App.messages.Add(new Message(String.Format("Cargando {0}", i), true, i));
                }
                App.messages.Clear();
            }
            catch (OperationCanceledException ex)
            {
                isRunning = false;
                App.messages.Clear();
                return;
            }
        }

Ventana que captura mensajería. En esta ventana solo se captura el evento cuando la colección cambia y la asignación de los valores al mensaje.

public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            App.messages.CollectionChanged += Messages_CollectionChanged; ;
        }
        delegate void MessageAddedHandler(Message _message);
        private void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    Dispatcher.Invoke(new MessageAddedHandler(setMessage), (Message)item);
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                Dispatcher.Invoke(new MessageAddedHandler(setMessage), new Message());
            }
        }
        void setMessage(Message message)
        {
            this.textBlock.Text = message.infoText;
            this.pb.Value = message.progress;
            this.pb.Visibility = message.progressIsVisible ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}

y con todo funcionando, cada vez que iniciemos desde la ventana principal el proceso, las dos ventanas deben mostrar el mismo texto y el mismo progreso.Os dejo el proyecto completo en el enlace.

Saludos

Sistema Planetario en Java

Sistema Planetario en Java

Para practicar POO, se me ha ocurrido crear un sistema planetario, el cual contiene unos objetos que a su vez orbitan alrededor de otro objeto con mayor gravedad y así poder calcular datos entre ellos, como pueda ser la fuerza de gravitación, distancias, tiempo de llegada, etc.

Este sistema es muy precario, porque no se tienen en cuenta las órbitas, si no que se entiende que todos los objetos está alineados y la distancia que los separa, es la distancia media de esas órbitas.

Con esta aplicación, existe una superclase abstracta ObjetoSistema que contiene la mayoría de los atributos y métodos, luego hereda de esta otra clase llamada ObjetoSinLuz al cual se le pueden añadir datos de su atmósfera mediante un ArrayList y de esta clase.

Los objetos reales del sistema, Estrella, hereda de la superclase; los planetas y satélites de la clase heredan de la clase ObjetoSinLuz para poder en un momento dado añadirles datos de su atmósfera.

Cada objeto tiene como atributo un objeto tipo ObjetoSistema que es el objeto de referencia sobre el cual orbita, de modo que la distancia media del objeto será a la de su objeto de referencia.

Para calcular la distancia por ejemplo entre la Luna y Marte, se calcularía:

  • La distancia entre la Luna y la Tierra que es su objeto de referencia
  • Distancia desde la Tierra al Sol y la suma de estas dos, la distancia de la luna al sol
  • Por otra parte calcularía la distancia  de Marte al Sol y la diferencia de ambas distancias, nos daría el resultado esperado.

El cálculo de las distancias se calcula recursivamente para que funcione en cualquier nivel, por ejemplo si la Luna a su vez fuera un objeto referencia de otros satélites o asteroides.

Otra particularidad que he incluido, es la posibilidad de obtener listados por diferentes órdenes, como pueda ser el caso de ordenar por la masa, tamaño, días de traslación, etc., de modo que el método compareTo de cada clase, debe tener previsto con que atributo ordenar.

Su funcionamiento es muy sencillo mediante menú textual, (lo que se trata es aprender)

**********************************************
Sistema Solar
**********************************************
* (1) Calcular distancia entre dos objetos   *
* (2) Calcular atracción gravitacional       *
* (3) Calcular tiempo (línea recta)          *
* (4) Listar objetos                         *
* (5) Salir                                  *
**********************************************

Introduzca la opción…
3
Introduzca el nombre del primer objeto…
tierra
Introduzca el nombre del segundo objeto…
marte
Introduzca la velocidad km/h (Si pulsa INTRO, se asignará 28000 km/h…
50000
Invertirá para recorrer 78.338.770 Km a una velocidad de 50.000 Km/h, 2 meses 5 días 6 horas 46 minutos 31 segundos  días
Pulse INTRO para continuar…

Tiempo empleado por la luz en llegar desde el Sol a Júpiter.(300.000km/s=1.080.000.000 km/h)

Invertirá para recorrer 778.412.010 Km a una velocidad de 1.080.000.000 Km/h, 43 minutos 14 segundos  días

o el mismo caso para el Sol y la Tierra

Invertirá para recorrer 149.597.870 Km a una velocidad de 1.080.000.000 Km/h, 8 minutos 18 segundos  días

Como caso particular, el código para calcular la distancia, que mediante un método recursivo, hasta que no encuentra el objeto de referencia principal, va calculando la distancia entre cualquier objeto y este.

/**
 * Funcion recursiva para calcular la distancia hasta el objeto de referencia del sistema planetario
 * @param objetoSistemaInicial Objeto inicial desde donde se calcula la distancia
 * @param objetoSistema Objeto hasta donde se calculará la distancia
 * @return Distancia entre los dos objetos
 */
private double calcularDistanciaAObjetoReferencia(ObjetoSistema objetoSistemaInicial){
        double distancia = objetoSistemaInicial.distanciaMedia;
        if (!objetoSistemaInicial.objetoReferencia.objetoReferencia.equals(objetoSistemaInicial.objetoReferencia)) {
            distancia += calcularDistanciaAObjetoReferencia(objetoSistemaInicial.objetoReferencia);        }
        return distancia;
    }





Os dejo el diagrama de clases y el
código (proyecto de NetBeans) para practicar y toquetear.

Un saludo y a programar.

Pd: No está terminada, ya que le falta añadir otros elementos para añadir componentes, atmósferas, etc., pero creo que se entiende su objetivo que es el de aprender a programar.

Le he incluido el sol, los planetas y dos satélites, la Luna y Phobos, meted más!

Espiral de Ulam

Espiral de Ulam

 

Un día durante una conferencia aburrida, un matemático llamado Stalisnaw Ulam jugando como no con los números, comenzó a dibujar en un papel el número 1 y girando alrededor de él en el sentido contrario a las agujas del reloj, siguió escribiendo el resto de números naturales consecutivamente formando así la espiral que lleva su nombre creando un cuadrado de n x n. Esta espiral tiene una particularidad y es que los números primos tienden a alinearse en diagonales. He aquí un ejemplo de como se forma, una espiral de 7 x 7 y una muestra mayor con los números primos remarcados:

37 36 35 34 33 32 31
38 17 16 15 14 13 30
39 18 5 4 3 12 29
40 19 6 1 2 11 28
41 20 7 8 9 10 27
42 21 22 23 24 25 26
43 44 45 46 47 48 49

Espiral de Ulam de orden 7

Espiral de Ulam con 142000 iteraciones

Por otra parte desde el punto de vista algorítmico, formar una espiral mediante código es otro reto, se puede hacer de múltiples formas, pero en concreto se me ha ocurrido crear un algoritmo de creación de la espiral y poder así implementar una ordenación con java implementado la interfaz Comparator y Comparable.

Para crear la espiral, lo primero determino la posición inicial del 1 dentro del cuadrado antes de formarlo. Esto es fácil, obtenemos el cuadrado del mayor impar y el valor de la mitad de los lados correspondería a las coordenadas x,y.

El paso siguiente sería rotar desde el 1 en el sentido de las agujas del reloj, formando cuadrados de n x n almacenando su valor en una clase que guardará las coordenadas y el valor de cada posición; este objeto, lo salvaremos en un ArrayList.

Como las coordenadas se van agregando en este orden en el ArrayList , una vez creada la espiral tendremos que ordenar las coordenadas para visualizarla en pantalla.

¿Qué clases usaremos?

Una clase Coordinate que almacena tres atributos, los valores de x, y y el valor de la coordenada. Implementa interface Comparable, por tanto debe contener el método compareTo .

Una clase que implementa la interfaz Comparator, por tanto debe implementar obligatoriamente el método compare y dentro de este llamaría al método compareTo de la clase Coordinate.

Diagrama de clases

¿Cómo funciona el algoritmo?

Del siguiente modo:

  • Iteramos n veces como cuadrados alrededor del 1 tengamos que dibujar
  • Iteramos 4 veces por cada uno de estos cuadrados, ya que un cuadrado tiene 4 lados y lo que haremos es decirle con esta iteración es la siguiente coordenada de destino donde tiene que asignarle un valor
  • Iteramos n veces por cada lado para escribir n números

Y una vez creada la espiral, ¿como ordenamos el ArrayList?

Una vez creada la espiral, debemos leer el ArrayList y visualizarlo en pantalla, pero antes hay que ordenarlo de modo que la primera coordenada sea la (0,0), la segunda (0,1), las siguientes (0,2)….(0,6), (1,0), (1,1)….(1,6),(2,0),…. hasta la (6,6) y para ello dentro del método compareTode la clase Coordinate.

Una vez ordenada el ArrayList, pasamos a imprimirla en pantalla con el método toString() iterando por la colección con un for con la salvedad de pasar a una nueva línea cada vez que se alcance el valor de n para formar el cuadrado.

Para otro día, un generador de espiral de Ulam con los números primos resaltados.

Aquí os dejo el código fuente de Java.

Un saludo

«Una ecuación no tiene para mí ningún significado a menos que exprese un pensamiento de Dios»

Srinivasa Ramanujan

package espiralulamproject;

/******************************************************
 *  @author:    Joaquín Martínez Rus (c) 2016
 *  @version:   1.0
 *  File:       Coordinate.java
 *  Created:    19/05/2016
 *  Project:    Ulam Spiral.
 *  Comments:   Clase Coordinate. Clase con coordenadas y valor asignados
 *
 *******************************************************/
public class Coordinate implements Comparable{

    // 

    /**
     * Inicia una nueva instancia de la clase Coordenada
     */
    public Coordinate(){
      this.x=0;
      this.y=0;
      this.value=0;
    }

    /**
     * Inicia una nueva instancia de la clase Coordenada
     * @param _coor Coordenada
     */
    public Coordinate(Coordinate _coor){
      this.x=_coor.x;
      this.y=_coor.y;
      this.value=_coor.value;
    }

    /**
     * Inicia una nueva instancia de la clase Coordenada
     * @param _x
     * @param _y
     */
    public Coordinate(int _x, int _y){
        this.x=_x;
        this.y=_y;
        this.value=0;
    }

    //  

    // 

    int x;
    int y;
    int value;

    //  

    // 

    /**
     * Obtiene una nuea coordenada a partir del origen
     * @param coordinate Coordenada
     * @return Coordenada
     */
    public Coordinate clone(Coordinate coordinate){
        return new Coordinate(coordinate.x, coordinate.y);
    }

    /**
     * Obtiene la Coordenada textual
     * @return Cadena de texto con la coordenada
     */
    @Override
    public String toString(){
        return "(" + x + ", "  + y + ") = " + this.value;
    }

    /**
     * Ordena dos coordenadas por su valor x e y
     * @param _coor Coordenada
     * @return Entero
     */
    @Override
    public int compareTo(Coordinate _coor) {
        int result=0;
        if (this.x  _coor.x) {
            result= 1;
        }
        else {
            if (this.y <=_coor.y) {
                result= -1;
            }
            else {
                result= 1;
            }
        }
        return result;
    }

    //
}

package espiralulamproject;
import java.util.Comparator;

/******************************************************
 *  @author:    Joaquín Martínez Rus (c) 2016
 *  @version:   1.0
 *  File:       SpiralComparator.java
 *  Created:    19/05/2016
 *  Project:    Ulam Spiral.
 *  Comments:   Clase SpiralComparator.
 *              Clase que implementa Comparator
 *******************************************************/
public class SpiralComparator implements Comparator<Coordinate> {
    @Override
    public int compare(Coordinate o1, Coordinate o2) {
        return o1.compareTo(o2);
    }
}

package espiralulamproject;
import java.util.ArrayList;

/******************************************************
 *  @author:    Joaquín Martínez Rus (c) 2016
 *  @version:   1.0
 *  File:       EspiralUlam.java
 *  Created:    19/05/2016
 *  Project:    Ulam Spiral.
 *  Comments:   Clase EspiralUlam
 *
 *******************************************************/
public class EspiralUlam {

    // 

    /**
     * Inicia unanueva instancia de la clase Espiral de Ulam
     * @param num Número máximo
     */
     public EspiralUlam(int num){
         baseNumber=getBaseNumber(num);
         maxNumber=getMaxNumber(baseNumber);
         firstNumber=getFirstNumbers(maxNumber,baseNumber);
         getFirstCoordinate();
         firstCoordinate.value=1;
         coordinates.add(firstCoordinate);
         setSpiral( new Coordinate(firstCoordinate));
     }
    // 

    // 

     int firstNumber;
     int baseNumber;
     int maxNumber;
     Coordinate firstCoordinate;
     public ArrayList<Coordinate> coordinates=new ArrayList();

    // 

    // 

    /**
     * Obtiene el resultado de la espiral
     * @return Cadena de texto
     */ @Override
     public String toString(){
         this.sort();
         int count=1;
         String textTemp="";
         String textOut="";
         for (Coordinate coordinate : coordinates) {
              textTemp = count % baseNumber == 0?"\n":"\t";
              textOut += coordinate.value + textTemp;
              count ++;
         }
         return textOut;
     }

    // 

    // 

    /**
     * Ordena el arrayList de números
     */
     private void sort(){
         coordinates.sort(new SpiralComparator());
     }

    /**
     * Obtiene el número base de la Espiral
     * @param number Número solicitado
     * @return Valor entero
     */
     private int getBaseNumber(int number){
         int n= (int)Math.sqrt((double)(number))+1;
         if (n%2==0) {
             n++;
         }
         return n;
     }

    /**
     * Obtiene el máximo número de la Espiral
     * @param number Número solicitado
     * @return Valor entero
     */
     private int getMaxNumber(int number){
         return number * number;
     }

    /**
     * Obtiene el primer número de la Espiral
     * @param iMaxNumber Máximo número de la espiral
     * @param ibaseNumber Número base de la espiral
     * @return Valor entero
     */
     private int getFirstNumbers(int iMaxNumber, int ibaseNumber){
         return iMaxNumber - 2*(ibaseNumber-1);
     }  

    /**
     * Obtiene la oordenada central de la espiral
     */
     private void getFirstCoordinate(){
         firstCoordinate=new Coordinate(baseNumber /2, baseNumber/2);
     }

    /**
     * Calcula la espiral de Ulam
     * @param _coor Coordenada central
     */
     private void setSpiral(Coordinate _coor){
         Coordinate temp=_coor;

         int lastNumber=1;
         for (int impar = 1; impar < baseNumber; impar +=2) {
              temp.x++;
              temp.y++;
              for (int lado = 0; lado < 4; lado++) {
              // 4 veces. una por lado del cuadrado
                  for (int i = 0; i <= impar; i++) {
                       temp.value= ++lastNumber;
                       coordinates.add(nextCoordinate(lado, temp));
                  }
              }
         }
     }

    /**
     * Obtiene la siguiente coordenada de la espiral
     * @param n Lado del cuadrado
     * @param _coor Coordenada de cálculo
     * @return Coordenada
     */
     private Coordinate nextCoordinate(int n, Coordinate _coor){
         switch (n) {
             case 0:
                 // UP. substract 1 to x
                 _coor.x--;
                 break;
             case 1:
                 //LEFT. substract 1 to y
                 _coor.y--;
                 break;
             case 2:
                 //DOWN. add 1 to x
                 _coor.x++;
                 break;
             case 3:
                 //RIGHT. add 1 to y
                 _coor.y++;
                 break;
         }
         return new Coordinate(_coor);
     }

}

Calculadora IP

Calculadora IP

Hola a tod@s

Aquí os muestro una pequeña calculadora de direcciones IP desarrollada en WPF y C# .NET 4.5 y con la que vemos una pequeña demostración de una clase y su funcionamiento.

CalIP01

El funcionamiento de la app es muy fácil, cuadros de texto para la IP y cuadro de texto para la máscara de subred con notación CIDR que quiere decir que el número introducido, indica el número de bits a 1 de la máscara de subred, es decir si introduzco 28, quiere decir que la máscara tiene 28 bit a 1, lo que sería:

11111111 11111111 11111111 11110000

el equivalente a 255.255.255.240.

A continuación adjunto la clase la cual no tiene ningún misterio (no he sido muy exquisito en cuanto a código, pero para aprender creo que está bien), propiedades públicas con los resultados y los métodos que si detallo:

CalIP02

Métodos públicos

  • SetNetAddress. Asigna la dirección de red
  • SetBroadCast. Asigna el Broadcast
  • SetIPt. Asigna la dirección ip
  • SetNetType. Asigna el tipo de red (A,B,C,D o E)
  • SetHostMin.Asigna a la propiedad el valor de host mínimo de la red
  • SetHostMax. Asigna a la propiedad el valor máximo de la red
  • GetIPToString. Obtiene la IP como un valor de cadena de la forma XXX.XXX.XXX.XXX
  • GetIPBinaryToString. Obtiene la IP como un valor de cadena en binario.

Métodos privados

  • NotifyPropertyChanged. Método delegado del evento cuando no es válida la IP. De esta manera, desde la clase donde instancio esta, puedo capturar si la IP no es válida e indicarlo.
  • BinaryToDecimal. Método sobrecargado para convertir números o direcciones ip de binario a decimal.
  • DecimalToBinary. Método sobrecargado para convertir números o direcciones ip de decimal a binario.
  • OperationOR. Operador OR sobre la dirección IP para obtener la máscara de entrada
  • OperationAND. Operador AND sobre la la dirección IP para obtener el broadcast
  • OperationNOT. Operador NOT sobre la la dirección IP para obtener el broadcast junto con el operador AND.
  • ValidateIP. Valida la IP, comprobando que sus valores son correctos y está bien formada.

Nota: Recordad que para obtener la dirección del red, se realiza la operación AND entre la IP y la máscara de subred y que para obtener el broadcast, se utiliza el operador OR entre la IP y la NOT de la máscara de subred (donde hay ceros, cambio a unos y viceversa).

OPERADOR AND
a
b
RESULTADO(Q)
0
0
0
0
1
0
1
0
0
1
1
1

 

OPERADOR OR
a
b
RESULTADO(Q)
0
0
1
0
1
1
1
0
1
1
1
0

 

OPERADOR NOT
a
RESULTADO(Q)
0
0
0
0

Si alguno ha dado el algebra de Boole, recordáis que a AND b= NOT (a OR b)?
solo hay que mirar la tabla AND y la OR, donde hay un 1 en la otra hay un 0!
y por último el símbolo de estos operadores (y de alguno más) es:
(una puerta NOR es una OR con una NOT, que es la que usamos para obtener el broadcast)

Para descargar la calculadora IP, pulsar aquí

Espero que os sirva y adjunto el código de la clase principal.

/* ************************************************************************************************************************************************
* © JOAQUIN MARTINEZ RUS 2015
* PROYECTO:         IpAddress Calculator
* Nombre:           AndrewWiles
* Archivo:          IpAddress.cs
* Descripción:      Clase principal
* Historial:
*                   1. Joaquin Martínez Rus - 13 ene 2016. Creación
*
* Comentarios:      Calculador de redes
*
*
**************************************************************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.ComponentModel;

namespace AndrewWiles
{
    public class IpAddress:INotifyPropertyChanged
    {
        public IpAddress()
        {
            CreateArrays();
            this.IP = "0.0.0.0";

        }

        public IpAddress(string ip)
        {
            CreateArrays();
            this.IP = ip;

        }

        #region Propiedades, eventos

        public event PropertyChangedEventHandler PropertyChanged;

        private string _ip;

        public string IP
        {
            get { return _ip; }
            set
            {
                _ip = value;

                IsValidIP = this.ValidateIP();

                if (IsValidIP)
                {
                    ipNumbers = _ip.Split('.');

                    for (int i = 0; i < ipNumbers.Length; i++)
                    {
                        ipNumbersByte[i] = DecimalToBinary(ipNumbers[i]);
                    }
                }
            }
        }
        public string[] ipNumbers { get; set; }
        public string[] ipNumbersByte { get; set; }
        public string[] NetWorkAddress { get; set; }
        public string[] Broadcast { get; set; }
        public string[] NetMask { get; set; }
        public string[] HostMin { get; set; }
        public string[] HostMax { get; set; }

        public string IpClass { get; set; }
        public string NetID { get; set; }
        public string HostID { get; set; }
        public int Hosts { get; set; }

        private int netMaskCIDR;

        public int NetMaskCIDR
        {
            get { return netMaskCIDR; }
            set
            {
                netMaskCIDR = value;
                this.ConvertCIDRToNetMask();
            }
        }

        private bool isValidIP;

        public bool IsValidIP
        {
            get { return isValidIP; }
            set
            {
                isValidIP = value;
                NotifyPropertyChanged("ValidIP");
            }
        }

        #endregion

        #region Métodos públicos

        public void SetNetAddress()
        {
            this.NetWorkAddress = this.OperationAND(this.ipNumbers, this.NetMask);
        }

        public void SetBroadCast()
        {
            this.Broadcast= this.OperationOR(this.ipNumbers, this.OperationNOT(this.NetMask));
        }

        public void SetIP(string ip)
        {
            this.IP = ip;
        }

        public void SetNetType()
        {
            if (ipNumbersByte[0].Substring(0,1)=="0")
            {
                this.IpClass = "A";
            }
            if (ipNumbersByte[0].Substring(0, 2) == "10")
            {
                this.IpClass = "B";
            }
            if (ipNumbersByte[0].Substring(0, 3) == "110")
            {
                this.IpClass = "C";
            }
            if (ipNumbersByte[0].Substring(0, 4) == "1110")
            {
                this.IpClass = "D";
            }
            if (ipNumbersByte[0].Substring(0, 4) == "1111")
            {
                this.IpClass = "E";
            }

        }

        public void SetHostMin()
        {
            this.NetWorkAddress.CopyTo(this.HostMin,0);
            this.HostMin[3]= (Convert.ToInt16(this.HostMin[3]) + 1).ToString();
        }

        public void SetHostMax()
        {
            this.Broadcast.CopyTo(this.HostMax, 0);
            this.HostMax[3] = (Convert.ToInt16(this.HostMax[3]) - 1).ToString();
        }

        public string GetIPToString(string [] ip)
        {
            string ipToString = "";

            foreach (var item in ip)
         {
                ipToString += item + ".";
         }

            return ipToString.Substring(0, ipToString.Length - 1);
        }

        public string GetIPBinaryToString(string[] ip)
        {
            string ipToString = "";

            foreach (var item in ip)
            {
                ipToString += this.DecimalToBinary(item) + "  ";
            }

            return ipToString.Substring(0, ipToString.Length - 1);
        }

        #endregion

        #region Métodos privados

        #region Métodos de Eventos

        private void NotifyPropertyChanged(string property)
        {
            var handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(property));
            }
        }

        #endregion

        #region Operaciones Binarias

        private string[] BinaryToDecimal(string[] aByte)
        {
            string[] bytes = new string[4];

            for (int i = 0; i < aByte.Length; i++)
            {
                bytes[i] = BinaryToDecimal(aByte[i]);
            }

            return bytes;
        }

        private string BinaryToDecimal(string aByte)
        {
            double aux = 0;

            for (int i = 0; i < aByte.Length; i++)
            {
                double potencia = Math.Pow(2, i);
                double digito=Convert.ToInt16(aByte.Substring(aByte.Length-1-i,1));
                aux += (potencia * digito);
            }

            return aux.ToString();
        }

        private string[] DecimalToBinary(string[] aByte)
        {
            string[] bytes = new string[4];

            for (int i = 0; i < aByte.Length; i++)
            {
                bytes[i] = DecimalToBinary(aByte[i]);
            }

            return bytes;
        }

        private string DecimalToBinary(string aByte)
        {
            int entero=Convert.ToInt32(aByte);
            return Convert.ToString(entero,2).PadLeft(8,'0');
        }

        public string[] OperationOR(string[] ip1, string[] ip2)
        {
            string[] CalculoOR = new string[4];

            for (int i = 0; i < ip1.Length; i++)
            {
                int a= Convert.ToInt32(ip1[i]);
                int b= Convert.ToInt32(ip2[i]);
                string o = (a | b).ToString();
                CalculoOR[i] = o;
            }

            return CalculoOR;
        }

        public string[] OperationAND(string[] ip1, string[] ip2)
        {
            string[] CalculoAND = new string[4];

            for (int i = 0; i < ip1.Length; i++)
            {
                int a = Convert.ToInt32(ip1[i]);
                int b = Convert.ToInt32(ip2[i]);
                string o = (a &amp;amp;amp;amp;amp; b).ToString();
                CalculoAND[i] = o;
            }

            return CalculoAND;
        }

        public string[] OperationNOT(string[] ip)
        {
            string[] CalculoNOT = new string[4];

            for (int i = 0; i < ip.Length; i++)              {                  string number = this.DecimalToBinary(ip[i]);                   string notNumber = "";                  foreach (var item in number)                  {                      notNumber += item == '0' ? '1' : '0';                  }                  CalculoNOT[i] = this.BinaryToDecimal(notNumber);              }              return CalculoNOT;          }           #endregion          #region Cálculos con Direcciones          private void ConvertCIDRToNetMask()          {              string unos = "";              string ceros = "";              int resto = 32 - this.NetMaskCIDR;              string netmaskTemporal = unos.PadRight(this.NetMaskCIDR, '1') + ceros.PadRight(resto, '0');              string a = netmaskTemporal.Substring(0, 8);              string b = netmaskTemporal.Substring(8, 8);              string c = netmaskTemporal.Substring(16, 8);              string d = netmaskTemporal.Substring(24, 8);              this.NetMask[0] = BinaryToDecimal(a);              this.NetMask[1] = BinaryToDecimal(b);              this.NetMask[2] = BinaryToDecimal(c);              this.NetMask[3] = BinaryToDecimal(d);              this.Hosts = (int)Math.Pow(2, resto) - 2;          }          #endregion          #region Validaciones          private bool ValidateIP()          {              return ValidateIP(this.IP);          }          private bool ValidateIP(string ip)          {              bool isValid = false;              Regex regexip = new Regex(@"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b");              MatchCollection result = regexip.Matches(ip);              if (result.Count > 0)
             {
                isValid = true;
             }
             else
             {
                isValid = false;
             }

            return isValid;
        }

        #endregion

        #region Genéricas

        private void CreateArrays()
        {
            ipNumbers = new string[4];
            ipNumbersByte = new string[4];
            NetWorkAddress = new string[4];
            Broadcast = new string[4];
            NetMask = new string[4];
            HostMin = new string[4];
            HostMax = new string[4];
        }

        #endregion

        #endregion
    }

    public class IPData
    {
        public IPData(string texto, string data, string binary)
        {
            this.Texto = texto;
            this.Data = data;
            this.Binary = binary;
        }

        public string Texto { get; set; }
        public string Data { get; set; }
        public string Binary { get; set; }

    }
}

Saludos

14.- Instrucciones de Salto

14.- Instrucciones de Salto
Hola a todos de nuevo. Hoy vamos a estudiar las instrucciones de salto. ¿Y que son las instrucciones de salto? Todos sabemos que en un programa las instrucciones se ejecutan secuencialmente, pues estas, nos permiten saltar de una parte del programa a otro evitando esa ejecución secuencial.Las instrucciones de salto para C# son:

  • break
  • continue
  • goto
  • return
  • throw

Y para VB.NET

  • Exit For
  • Exit Do
  • Exit While
  • Exit Select
  • Continue For
  • goto
  • Return

Paso a paso, vamos a detallar una por una excepto la última que la incluiremos en el tratamiento de errores.

La instrucción break nos permita terminar un bucle o salir de una instrucción switch, es decir por ejemplo, si creamos un bucle do while que vaya sumando 1 a una variable y le ponemos la condición de que cuando esa variable llegue a 100 salga del bucle, pues en la parte verdadera de esa condición es donde debemos poner el break, para que así salga y no siga sumando. Para VB.NET, se utiliza Exit For para un bucle For Next, Exit Do para un bucle Do While, Exit While para un bucle While y Exit Select para una instrucción Select Case. Como veis, C# se complica menos y utiliza break para todos los casos.

for (int i = 1; i <= 1000; i++)
{
	if (i == 100)
	{
		break;
	}
	Console.WriteLine(i);
}

Mismo caso pero con una instrucción while

int n = 0;
while (true)
{
	n += 1;
	if (n==100)
	{
		break;
	}
}

Y su equivalente para Vb.NET

        For i As Integer = 1 To 1000
            If i = 100 Then
                Exit For
            End If
            Console.WriteLine(i)
        Next
        Dim n As Integer = 0
        While True
            n += 1
            If n = 100 Then
                Exit While
            End If
        End While

continue, es una instrucción que no suelo utilizar a menudo, pero es curiosa en cuanto a su funcionamiento. Esta lo que provoca dentro de un bucle for o foreach, pasar a la siguiente iteración, por ejemplo.

            for (int i = 1; i <= 10; i++)
            {
                if (i < 9)
                {
                    continue;
                }
                Console.WriteLine(i);
            }

En este ejemplo cuando i es menor que 9 la instrucción continue evita que se escriba en la consola pasando a darle un valor nuevo a i hasta que i es mayor o igual a 9, donde entonces se imprimirá el valor en pantalla. Su equivalente en VB.NET es Continue For.

        For i As Integer = 1 To 10
            If i < 9 Then
                Continue For
            End If
            Console.WriteLine(i)
        Next

Goto transfiera o envía el control del programa a un sitio concreto del programa y ¿cómo sabe donde tiene que enviarlo?, pues porque en ese lugar debe existir una etiqueta. También puede usarse para transferir el control de programa a una etiqueta de una instrucción switch. Además, como es normal, podemos usarla también para salir de bucles, ¿no crees? Exponemos ejemplos. En el primero, aparecerá en pantalla ‘He salido’ cuando el valor de i valga 9.

C#

            for (int i = 1; i <= 10; i++)
            {
                if (i > 8)
                {
                    goto SalidaPrograma;
                }
            }
        SalidaPrograma:
            Console.WriteLine("He salido");

VB.NET


        For i As Integer = 1 To 10
            If i > 8 Then
                GoTo SalidaPrograma
            End If
        Next
SalidaPrograma:
        Console.WriteLine("He salido")

En el caso de salto dentro de una instrucción switch, lo vemos a continuación en el que dependiendo del valor de n, (el resto no me valen), en el que para el caso de 2 y 3, efectuará la suma propia del caso, pero además lo reenviará al valor 1 donde volverá a sumarle 25, ¿ok?
C#

            int valor=0;
            switch (n)
            {
                case 1:
                    valor += 25;
                    break;
                case 2:
                    valor += 25;
                    goto case 1;
                case 3:
                    valor += 50;
                    goto case 1;
                default:
                    Console.WriteLine("Esto no me vale");
                    break;
            }

VB.NET


        Dim valor As Integer = 0
        Select Case n
            Case 1
                valor += 25
                Exit Select
            Case 2
                valor += 25
             goto case 1
            Case 3
                valor += 50
             goto case 1
            Case Else
                Console.WriteLine("Esto no me vale")
                Exit Select
        End Select

La instrucción return acaba la ejecución del método donde se ha ejecutado y devuelve el control al método que llamó antes, además puede devolver algún valor. Con un ejemplo lo veremos con más claridad.

Si ejecutamos el método Inicio, se llamará al método CalcularAreaCirculo con un valor de radio igual a 5. Dentro del método de cálculo, se efectuarán los cálculos pertinentes para obtener el área y acto seguido se ejecuta return area, devolviendo el valor del área al método que lo llamó que es Inicio. Una vez este tiene el valor, lo visualiza en pantalla. Lo que si tenemos que tener en cuenta para VB.NET, es que para que un método devuelva un valor, debe ser una función (Function), cosa que en C# en vez de void se le asigna un tipo.

C#

            private void Inicio()
            {
                int radius = 5;
                double result = CalcularAreaCirculo(radius);
                Console.WriteLine("El area es {0:0.00}", result);
                Console.ReadKey();
            } 

            private double CalcularAreaCirculo(int r)
            {
                double area = r * r * Math.PI;
                return area;
            }

VB.NET

    Private Sub Inicio()
        Dim radius As Integer = 5
        Dim result As Double = CalcularAreaCirculo(radius)
        Console.WriteLine("El area es {0:0.00}", result)
        Console.ReadKey()
    End Sub

    Private Function CalcularAreaCirculo(r As Integer) As Double
        Dim area As Double = r * r * Math.PI
        Return area
    End Function

Pues esto es todo en cuanto a las instrucciones de salto, fácil, ¿no?. Pues nos vemos en la próxima entrega!!

Saludos “der Waki”

13.- Instrucciones de iteración

13.- Instrucciones de iteración

Por fin he vuelto. Estimados amigos, he tenido que dejar por una temporada el blog de Aprender a programar para mejorar mis conocimientos anglo-lingüísticos y me han absorbido todo el tiempo del mundo, pero a partir de ahora… si nada ni nadie lo impide, seguiremos en primera línea. Vamos allá.Bueno pues las instrucciones de iteración nos permiten repetir ciertas acciones o crear un bucle. Un bucle se puede repetir un número de veces limitado porque aunque se puede hacer ilimitado no tiene ningún sentido crear un programa que no acabe nunca, ¿no?. Dentro de estos bucles se ejecutan las instrucciones ordenadamente salvo cuando utilizamos otro tipo de instrucciones que son las de salto.

Tenemos varios tipos de instrucciones de salto y la primera de todas es do-while (hacer mientras…). Ahora vamos a ver un ejemplo para C# y su equivalente para VB .NET. Esta instrucción ejecuta un bloque de instrucciones repetidas veces hasta que una determinada instrucción se evalúa como false. Para C# el bloque de código se incluye entre llaves y acto seguido se incluye la expresión booleana. En el siguiente ejemplo creamos una pequeña aplicación que imprimirá por pantalla los valores desde 0 a 2. Como veis, al entrar en el bucle, se imprime el primer valor de x que es cero, después le suma a x una unidad (x++ esto de verdad que suma una unidad, es un operador y lo veremos en un futuro) , efectúa la comprobación x vale ahora 1, ¿1 es menor que 3?, si (true), pues volvemos al inicio del bucle do y se vuelve a ejecutar el mismo código hasta que x llega a tener el valor de 3 que en ese caso 3 no es menor que 3 y la expresión booleana sería false. En este momento, saldría del bucle. Para VB.NET es igual pero con Do y Loop While donde el contenido de código incluido entre ambas instrucciones, es lo que se ejecutará mientras la expresión While sea false. El aspecto fundamental de esta instrucción es que por lo menos, el bloque de código se ejecuta una sola vez ya que una vez ejecutado es cuando se comprueba la expresión booleana que permitirá seguir ejecutando este o salir del bucle.

C#

public class DoWhile_Prueba
    {
        public static void Main()
        {
            int x = 0;
            Console.WriteLine("SALIDA EN PANTALLA");
            do
            {
                Console.WriteLine(x);
                x++;
            } while (x &lt; 3);
            Console.WriteLine("FIN DE BUCLE");
        }
    }
    /* SALIDA EN PANTALLA:
      	0
        1
        2
    */

Vb.NET

Public Class DoWhile_Prueba
    Public Shared Sub Main()
        Dim x As Integer = 0
        Console.WriteLine("SALIDA EN PANTALLA")
        Do
            Console.WriteLine(x)
            x += 1
        Loop While x &lt; 3
        Console.WriteLine("FIN DE BUCLE")
    End Sub
End Class
'
'        SALIDA EN PANTALLA:
'        0
'        1
'        2

Ahora vamos a ver la instrucción while y su equivalente en VB.NET While y End While. En este tipo de instrucciones, la expresión booleana se verifica al principio, por tanto si al comienzo no se verifica la expresión , no se ejecutará ni una sola vez el contenido de código del bucle (esta es la principal diferencia con Do-While). Con esta instrucción, he incluido un cosita nueva, en la anterior instrucción metimos un operador de adición y en esta un parámetro. Cuando le damos formato a un texto como es en el siguiente caso, una vez vale una cosa y a la siguiente vale otra, bueno pues utilizamos unas llaves con un número en su interior y cuando se ejecute, se buscará por orden el primer parámetro disponible. Un ejemplo y lo entenderéis.

Console.WriteLine("Hola {0} {0}, me llamo {1} {2}, y esto es una {3}", "amigos", "Joaquín", "Martínez", "prueba");

{0} equivale a amigos, {1} a Joaquín, {2} a Martínez y {3} a prueba.
Esto imprimirá en pantalla “Hola amigos amigos, me llamo Joaquín Martínez y esto es una prueba”. Si os dais cuenta en cada hueco donde antes existía una llave y un número, ahora existe su equivalente en texto con el mismo orden en el que encontraba. Aunque no lo parezca, esto es muy útil!

C#

class WhileTest_Prueba
    {
        static void Main()
        {
     Console.WriteLine("Salida en pantalla")
            int n = 1;
            while (n < 6)
            {
                Console.WriteLine("Valor actual de n es {0}", n);
                n++;
            }
        }
    }
    /* Salida en pantalla:
        Valor actual de n es 1
        Valor actual de n es 2
        Valor actual de n es 3
        Valor actual de n es 4
        Valor actual de n es 5
     */

VB.NET

Class WhileTest_Prueba
    Private Shared Sub Main()
        Dim n As Integer = 1
        Console.WriteLine("Salida en pantalla")
        While n < 6
            Console.WriteLine("Valor actual de n es {0}", n)
            n += 1
        End While
    End Sub
End Class
'
'        Salida en pantalla:
'        Valor actual de n es 1
'        Valor actual de n es 2
'        Valor actual de n es 3
'        Valor actual de n es 4
'        Valor actual de n es 5

Ojo con este tipo de instrucciones porque SIEMPRE deben tener una salida, es decir que alguna vez se debe cumplir el valor de la expresión en false para poder salir del bucle, porque si no se convertiría en un bucle infinito sin ninguna utilidad. En el siguiente ejemplo ocurriría esto. Ya que el valor de nes 4 y la instrucción se ejecutará mientras que n sea mayor que 3, por tanto entraría en un bucle sin fin. Otra forma de salir de un bucle es mediante break, goto o return para C# o Exit While o Exit For, GoTo o Return.

C#

class WhileTest_Prueba
    {
        static void Main()
        {
            int n = 4;
            while (n > 3)
            {
                Console.WriteLine("Valor actual de n is {0}", n);
                n++;
            }
        }
    }

VB.NET

Class WhileTest_Prueba
    Private Shared Sub Main()
        Dim n As Integer = 1
        Console.WriteLine("Salida en pantalla")
        While n < 6
            Console.WriteLine("Valor actual de n is {0}", n)
            n += 1
            Return
        End While
    End Sub
End Class
'
'        Output:
'        Valor actual de n es 1
'        Valor actual de n es 2
'        Valor actual de n es 3
'        Valor actual de n es 4
'        Valor actual de n es 5

Otra instrucción de iteración, for para C# y For Next para VB.NET. Estas se ejecutan n-veces hasta que una expresión sea false y son muy útiles para recorrer matrices o para casos en los que conocemos el número de veces que se va a repetir un bucle. Por ejemplo vamos a ejecutar una instrucción 5 veces. La instrucción for realiza varias funciones. El primero y solo se hace una vez es declarar un variable i asignándole el valor de 1 (podríamos asignarle el valor 34 o -23), después la expresión de evaluación en este caso i<=5, es decirse ejecutará hasta que el valor de i sea menor o igual que 5 y por último el valor siguiente después de ejecutar el bloque que en este caso incrementa en 1 el valor de i (podríamos restarle 1 a i mediante –i o sumarle 2 mediante i+=2 aunque para VB.NET se utiliza Step.
Nota: La diferencia entre ++i y i++ es que la suma se realiza antes o después de asignarle un valor. Os muestro un ejemplo:

int i=3;
int j= ++i;
Console.WriteLine("Valor actual de j es {0}", j)
// Valor actual de j es 4;
i=3;
j= i++;
Console.WriteLine("Valor actual de j es {0}", j)
// Valor actual de j es 3;

Para VB.NET cerramos el bloque de código mediante la instrucción Next.
C#

class For_Prueba
    {
        static void Main()
        {
            Console.WriteLine("Salida");
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine(i);
            }
        }
    }
    /* Salida:
    1
    2
    3
    4
    5
    */

Class For_Prueba
    Private Shared Sub Main()
        Console.WriteLine("Salida")
        For i As Integer = 1 To 5
            Console.WriteLine(i)
        Next
    End Sub
End Class
'
'    Salida:
'    1
'    2
'    3
'    4
'    5

Ejemplo de for obteniendo los números pares hasta 100
C#

for (int i = 1; i <= 100; i+=2)
{
       Console.WriteLine(i);
}

VB.NET

For i As Integer = 1 To 100 Step 2
       Console.WriteLine(i)
Next

Por último la instrucción foreach se utiliza para recorrer colecciones y ¿qué es una colección? Pues imaginemos que miramos la galería de fotos de nuestro móvil, seleccionamos una y vamos pasando una a una. Pues esto es lo que hace la instrucción foreach, pasar de una foto a otra por toda la colección.

¿Y qué hace? Pues el bloque de código contenido, se ejecutará para cada elemento de la colección. En el siguiente ejemplo, creamos una colección de números enteros y rotamos por cada uno de ellos. Como veis, esta instrucción tiene también varios elementos primero foreach, segundo y se ejecuta cada vez, crea un objeto del mismo tipo que los elementos de la colección y le asigna el valor de uno de estos y por último dice de donde debe extraer esos objetos. Pues para el siguiente caso se imprimirá por pantalla 0,1,1,2,3,5,8 y 13. Y a continuación expongo otra forma de recorrer una colección mediante una instrucción for diciendo que desde el valor con índice 0 hasta el valor menor que el número de elementos que tiene, imprima en pantalla el valor con el índice de la instrucción, curioso ¿no?

Para VB.NET foreach se convierte en For Each el valor del elemento declarado y la colección desde donde se extraerán los elementos acabando y cerrando el bloque de código con Next.
C#

int[] coleccion = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in coleccion)
{
         System.Console.WriteLine(element);
}
for (int i = 0; i < coleccion.Length; i++)
{
         System.Console.WriteLine(coleccion[i]);
}
/* En ambos casos, se imprime:
    0
    1
    1
    2
    3
    5
    8
    13
    */

Dim coleccion As Integer() = New Integer() {0, 1, 1, 2, 3, 5, 8, 13}
For Each element As Integer In coleccion
          System.Console.WriteLine(element)
Next
For i As Integer = 0 To coleccion.Length - 1
          System.Console.WriteLine(coleccion(i))
Next

Pues esto esto esto es todo amigos!!

Saludos “der Waki”

 

12.- Instrucciones de Selección

12.- Instrucciones de Selección
Las instrucciones son acciones que realiza el programa. Existen acciones comunes como la declaración de variables, la asignación de valores, la llamada a métodos, el recorrido en bucle de una colección y la creación de bifurcaciones a uno u otro bloque de código en función de una condición. El orden en que se ejecutan las instrucciones en un programa se denomina flujo de control o flujo de ejecución.Para C#, una instrucción puede estar compuesta por una única línea de código que finaliza en un punto y coma o por una serie de instrucciones de una línea incluidas en un bloque. Como ya hemos visto, un bloque de instrucciones se encierra entre llaves {} y pueden contener bloques anidados. A continuación enumero las categorías:

  • Instrucciones de selección (if, else, switch, case)
  • Instrucciones de iteración (do, for, foreach, in, while)
  • Instrucciones de salto (break, continue, default, goto, return, yield)
  • Instrucciones de control de excepciones (throw, try-catch, try-finally, try-catch-finally)
  • Checked y unchecked (checked, unchecked)
  • Instrucción fixed (fixed)
  • Instrucción lock

En VB.NET

En Visual Basic, una instrucción es una instrucción completa.  Puede contener palabras clave, operadores, variables, constantes y expresiones y cada instrucción como en C#, pertenece a cada una de las categorías siguientes:

Instrucciones de declaración, que dan nombre a una variable, constante o procedimiento y también pueden especificar un tipo de datos.

Instrucciones ejecutables, que inician acciones.  Estas instrucciones pueden llamar a un método o función, y pueden repetirse en bloques de código o crear una bifurcación en otro bloque de código. Las instrucciones ejecutables incluyen instrucciones de asignación, que asignan un valor o expresión a una variable o constante.

En este capítulo, vamos a ver las instrucciones de selección para C# y las instrucciones ejecutables para VB.NET.

¿Empezamos por C#? Venga, pues una instrucción de selección, hace que el programa vaya a un determinado punto dependiendo de que cierta condición sea verdadero o falso (true o false). Las palabras if, else, switch, case y default son las palabras clave en las instrucciones de selección. Como son palabras clave, no pueden usarse como variables o nombres de métodos ya que el compilador nos daría un error.

La instrucción if identifica que sentencia debe ejecutarse dependiendo de un valor booleano (verdadero o falso, true o false), vemos un ejemplo:

bool condition = true;
if (condition)
{
       Console.WriteLine("La variable tiene un valor true");
}
else
{
       Console.WriteLine("La variable tiene un valor false.");
}

Analicemos el código. Si la variable es true, (verdadera), visualizaremos en pantalla el texto de “La variable tiene un valor true”, pero si el valor lo cambiáramos a false, veríamos el segundo mensaje “La variable tiene un valor false”. El contenido donde se dirigirá el flujo del programa, se encuentra encerrado entre llaves {}, a continuación tenemos la palabra clave else y otras dos llaves con el contenido donde se redirigirá el programa cuando la condición es false.

En el siguiente ejemplo, declaramos una variable con valor entero y le asignamos el valor 8 y en la instrucción comparamos ese valor m para ver si es mayor que 10, ¿es mayor 8 que 10 (entre paréntesis)? Yo creo que no, pues entonces se aparecerá el mensaje “La variable tiene un valor false”.

int m = 8;
if (m > 10)
{
        Console.WriteLine("La variable tiene un valor true");
}
else
{
        Console.WriteLine("La variable tiene un valor false.");
}

También podríamos comparar si un texto es igual a otro mediante el operador ==

string texto = “mi_texto”;
if (texto == “mi_texto”)
{
	Console.WriteLine("La variable tiene un valor true");
}

También existe una combinación de if else y es que además podemos hacer una comparativa if y si falla esta, podemos hacer de nuevo otra comparación else if y si fallaran todas las instrucciones else if, redirigir el programa a la instrucción else.

int m = 8;
if (m > 10)
{
	Console.WriteLine("La variable tiene un valor true");
}
else if (m == 8)
{
	Console.WriteLine("La variable tiene un valor igual a 8");
} 
else
{
	Console.WriteLine("La variable tiene un valor false.");
}

La instrucción if tiene una forma simplificada como la que os represento

if (m > 10) {Console.WriteLine("La variable tiene un valor true");}
else {Console.WriteLine("La variable tiene un valor false.");}

(Esta versión es igual que las anteriores, pero con las llaves en la misma línea)
o

if (m > 10) Console.WriteLine("La variable tiene un valor true");
else Console.WriteLine("La variable tiene un valor false.");

Consejo: cuando no sepáis como funciona una instrucción, acudid a la referencia de msdn, ahí tenéis todo.

Otra instrucción de selección es switch en la que se redirige el programa a la lista de candidatos, en el siguiente caso, como es igual a 1, se redirigirá a la zona donde se imprimirá en pantalla “Caso 1”. Si por ejemplo el valor de la variable tuviera un valor de 17, como no existe ningún caso que valga 17, se ejecutaría la instrucción Default.

La palabra clave break, hace que una vez ejecutado el caso concreto, el programa se redirija fuera de la instrucción y no ejecute nada más dentro de esta.

int casoSwitch = 1;
switch (casoSwitch)
{
            case 1:
                Console.WriteLine("Caso 1");
                break;
            case 2:
                Console.WriteLine("Caso 2");
                break;
            default:
                Console.WriteLine("Caso Default");
                break;
}

En el lenguaje “der Waki”. ¿Existe algún caso que sea igual a 2? Pués claro!! Ir hasta el caso 2 e imprimir en pantalla “Caso 2”.

Otro caso, sería poder agrupar casos. En este concreto, hemos agrupado el caso 2,3 y 8, de modo que si la variable vale alguno de ellos, aparecerá el texto “Caso 2, 3 u 8”

        int casoSwitch = 3;
        switch (casoSwitch)
        {
            case 1:
                Console.WriteLine("Caso 1");
                break;
            case 2:
            case 3:
            case 8:
                Console.WriteLine("Caso 2, 3 u 8");
                break;
            default:
                Console.WriteLine("Caso Default");
                break;
        }

En Visual Basic .NET es muy parecido todo, pero existen algunas variaciones, primero que no existen llaves como en C#, estas se cambian por palabras clave, entonces para iniciar una instrucción if, esta comienza por lo mismo If, se añade la condición (sin paréntesis), palabra clave Then (entonces en inglés), el contenido del código que se ejecutará cuando sea verdadero, ElseIf o Else cuando queremos efectuar nuevas comparativas o la condición es falsa respectivamente y por fin le decimos al compilador que la instrucción finaliza con End If. ¿Bien? Prácticamente es igual!

Dim m As Integer = 8
If m > 10 Then
	Console.WriteLine("La variable tiene un valor true")
ElseIf m = 8 Then
	Console.WriteLine("La variable tiene un valor igual a 8")
Else
	Console.WriteLine("La variable tiene un valor false.")
End If

Para la instrucción switch iniciamos con Select Case y expresión Switch. Luego los casos, código del caso y salida de la instrucción Select Case.

Dim casoSwitch As Integer = 3
Select Case casoSwitch
	Case 1
		Console.WriteLine("Caso 1")
		Exit Select
	Case 2, 3, 8
		Console.WriteLine("Caso 2, 3 u 8")
		Exit Select
	Case Else
		Console.WriteLine("Caso Default")
		Exit Select
End Select

Igual pero con otras palabras!! Same, but with other words!!
Y si encima .NET, te permite escribir en varios lenguajes, pues que más da el que uses!! el que más fácil te resulte, ¿no?

Saludos “der Waki”

Nota: Si utilizáis Visual Studio, escribid dentro de un método la palabra if y pulsáis la tecla TAB dos veces. OHHH!! el fragmento de código, se escribe solo!!!