Está en la página 1de 34

-- REPLICATE() Repite un valor de cadena un nmero especificado de veces.

4> SELECT Name + REPLICATE('*', 20 - LEN(Name)) FROM Employee 5> GO Jason*************** Robert************** Celia*************** Linda*************** David*************** James*************** Alison************** Chris*************** Mary**************** En el ejemplo siguiente se replica un carcter 0 cuatro veces delante de un cdigo de lnea de produccin en la base de datos AdventureWorks2012. USE AdventureWorks2012; GO SELECT [Name] , REPLICATE('0', 4) + [ProductLine] AS 'Line Code' FROM [Production].[Product] WHERE [ProductLine] = 'T' ORDER BY [Name]; GO

El conjunto de resultados es el siguiente. Name -------------------------------------------------HL Touring Frame - Blue, 46 HL Touring Frame - Blue, 50 HL Touring Frame - Blue, 54 HL Touring Frame - Blue, 60 HL Touring Frame - Yellow, 46 HL Touring Frame - Yellow, 50

Line Code --------0000T 0000T 0000T 0000T 0000T 0000T

En el ejemplo siguiente se rellena de nmeros a la izquierda hasta una longitud especificada mientras que los nmeros se convierten de un tipo de datos numrico a caracteres o Unicode. USE AdventureWorks2012; GO IF EXISTS(SELECT name FROM sys.tables WHERE name = 't1') DROP TABLE t1; GO CREATE TABLE t1 ( c1 varchar(3), c2 char(3) ); GO INSERT INTO t1 VALUES ('2', '2'), ('37', '37'),('597', '597'); GO SELECT REPLICATE('0', 3 - DATALENGTH(c1)) + c1 AS 'Varchar Column', REPLICATE('0', 3 - DATALENGTH(c2)) + c2 AS 'Char Column' FROM t1; GO

Introduccin a las clases en C# Como hemos dicho, C# es un lenguaje orientado a objetos. A diferencia de lenguajes como C++ o Python en los que la orientacin a objetos es opcional, en C# y al igual que en Java, la orientacin a objetos es ineludible, de hecho cualquier mtodo o variab le est contenida dentro de un objeto. Y el concepto fundamental en torno a la orientacin a objetos es la clase.

Una clase es como una plantilla que describe cmo deben ser las instancias de dicha clase, de forma que cuando creamos una instancia, sta tendr exactamente los mismos mtodos y variables que los que tiene la clase. Los datos y mtodos contenidos en una clase se llaman miembros de la clase y se accede a ellos siempre mediante el operador "." . En el siguiente ejemplo, se definir una clase, Clase1 y en el mtodo Main se crear una instancia de Clase1 llamada MiClase. Una buena idea es jugar un poco con el cdigo para ver que la instancia de la clase efectivamente tiene los mismos miembros que la clase Clase1 (que sera la plantilla de la que hablbamos antes) using System; //definimos nuestra clase class Clase1{ public int a = 1; private double b = 3; public char c = 'a'; } //usamos la clase que hemos creado class UsoClase{ public static void Main() { Clase1 MiClase = new Clase1(); // asi creamos una instancia de Clase1 Console.WriteLine( MiClase.c ); //podemos llamar a los tipos que hay dentro de Clase1 } } los identificadores public delante de los tipos que hay dentro de Clase1 son necesarios para luego poder ser llamados desde otra clase, como en este caso, que estamos llamando a los miembros de una instancia de Clase1 desde UsoClase. Pero en las clases no solo hay variables, tambin podemos incluir mtodos. using System; //definimos nuestra clase class Clase1{ public int a = 1; public double b = 3; public char c = 'a'; public void Descripcion() { Console.WriteLine("Hola, soy una clase"); } } //usamos la clase que hemos creado class UsoClase{ public static void Main() { Clase1 MiClase = new Clase1(); // asi creamos una instancia de Clase1 Console.WriteLine( MiClase.c ); //podemos usar todos los tipos que hay dentro de Clase1 MiClase.Descripcion(); } } Podemos hacer ms cosas con las clases, como heredar otras clases o implementar interfaces, pero en este captulo nos centraremos en el uso de mtodos y variables. Mtodos Los mtodos, tambin llamados funciones, son trozos de cdigo que reciben unos datos, hacen algo con esos datos, y a veces devuelven algn valor. En C#, todos los mtodos se encuentran contenidos dentro de un objeto. La estructura mnima de un mtodo tiene las siguientes partes: * Tipo devuelto * Nombre del mtodo * Parmetros (puede ser vaco) * Cuerpo del mtodo de forma que el siguiente mtodo: double Divide( double a, double b ) { return a/b; } devuelve un tipo double, tiene por nombre Divide, los parmetos son a y b, ambos del tipo double, y el cuerpo del mtodo es simplemente "return a/b;".

Cuando queramos llamar a un mtodo, debemos simplemente poner el nombre del mtodo y sus argumentos dentro de un parntesis separados por comas. Para llamar al mtodo Divide declarado antes, simplemente debemos escribir Divide(8, 2); Segn lo que hemos visto, el ejemplo del mtodo Divide() completo necesita tener una clase donde definirse y un mtodo Main() donde ejecutarse. using System; class Metodo{ public double Divide( double a, double b ) { return a/b; } } class Principal{ public static void Main() { Metodo m = new Metodo(); Console.WriteLine( m.Divide(8, 2) ); } } Pasando valores a los mtodos Parmetros La declaracin formal de parmetros tambin define variables. Hay cuatro tipos de parmetros: parmetros por valor, por referencia, parmetros de salida, y arreglos de parmetros. Paso por valor El paso de parmetros por valor es usado por defecto para pasar parmetros a mtodos. Cuando se pasa un parmetro por valor a una funcin realmente se est pasando una copia de dicho parmetro, por lo que las modificaciones que le hagamos al parmetro dentro del mtodo no afectarn al parmetro original. El ejemplo using System; class Test { static void F(int p) { p++; Console.WriteLine("p = {0}", p); } static void Main() { int a = 1; Console.WriteLine("pre: a = {0}", a); F(a); Console.WriteLine("post: a = {0}", a); } } muestra un mtodo F que tiene un parmetro por valor llamado p. El ejemplo produce la salida: pre: a = 1 p=2 post: a = 1 aunque el valor del parmetro p haya sido modificado dentro del mtodo, ste parmetro solamente tena una copia del valor del parmetro a que pasamos al mtodo; por lo que cuando imprimimos el parmetro a vemos que ste parmetro ha mantenido su valor original. Paso por referencia El paso de parmetros por referencia es la contraposicin lgica al paso por valor. En el paso por referencia no se realiza ninguna copia del objeto, sino que lo que se le pasa a la funcin es una referencia del objeto, de forma que el parmetro pasa directamente a la funcin y cualquier modificacin sobre el parmetro dentro de la funcin afectar al parmetro original using System; class Test { static void Swap(ref int a, ref int b) { // intercambia los dos valores int t = a; a = b; b = t; } static void Main() { int x = 1; int y = 2; Console.WriteLine("pre: x = {0}, y = {1}", x, y);

Swap(ref x, ref y); Console.WriteLine("post: x = {0}, y = {1}", x, y); } } muestra un mtodo swap que tiene dos parmetros por referencia. La salida producida es: pre: x = 1, y = 2 post: x = 2, y = 1 La palabra clave ref debe de ser usada tanto en la declaracin formal de la funcin como en los usos que se hace de sta. Parmetro de salida El parmetro de salida es similar al parmetro por referencia, salvo que el valor inicial de dicho argumento carece de importancia. Un argumento de salida se declara con el modificador out. El ejemplo using System; class Test { static void Divide(int num1, int num2, out int result, out int resid) { result = num1 / num2; resid = num1 % num2; } static void Main() { int valor1 = 10; int valor2 = 3; int respuesta, residuo; Divide(valor1, valor2, out respuesta, out residuo); Console.WriteLine("La divisin de {0} para {1} = {2} con un residuo de {3}", valor1, valor2, respuesta, residuo); } } muestra un mtodo Divide que incluye dos parmetros de salida. Uno para el resultado (variable result) de la divisin y otro para el resto (variable resid). Vemos que estos resultados son asignados a las variables respuesta y residuo respectivamente. Arreglo de parmetros Habr ocasiones que necesitemos pasar varios parmetros a un mtodo (o funcin) pero no sabremos con anticipacin cuantos parmetros tendremos que pasar; para esto podremos usar un arreglo de parmetros. Un arreglo de parmetros permite guardar una relacin de varios a uno: varios argumentos pueden ser representados por un nico arreglo de parmetros. En otras palabras, los arreglos de parmetros permiten listas de argumentos de tamao variable. Un arreglo de parmetros se declara con el modificador params. Slo puede haber un arreglo de parmetros en cada mtodo, y siempre debe ser el ltimo parmetro especificado. El tipo del arreglo de parmetros debe ser siempre un tipo arreglo unidimensional. Al llamar a la funcin se puede pasar uno o varios argumentos del tipo del arreglo. El ejemplo using System; class Test { static void F(params int[] args) { Console.WriteLine("n de argumentos: {0}", args.Length); for (int i = 0; i < args.Length; i++) Console.WriteLine("args[{0}] = {1}", i, args[i]); } static void Main() { F(); F(1); F(1, 2); F(1, 2, 3); F(new int[] {1, 2, 3, 4}); } } muestra un mtodo F que toma un nmero variable de argumentos int, y varias llamadas a este mtodo. La salida es: n de argumentos: 0 n de argumentos: 1 args[0] = 1 n de argumentos: 2 args[0] = 1 args[1] = 2 n de argumentos: 3 args[0] = 1 args[1] = 2 args[2] = 3 n de argumentos: 4 args[0] = 1 args[1] = 2 args[2] = 3

args[3] = 4 La mayora de los ejemplos presentes en este captulo utilizan el mtodo WriteLine de la clase Console. El comportamiento para las sustituciones, como muestra el ejemplo int a = 1, b = 2; Console.WriteLine("a = {0}, b = {1}", a, b); se consigue usando un arreglo de parmetros. El mtodo WriteLine proporciona varios mtodos sobrecargados para el caso comn en el que se pasa un pequeo nmero de argumentos, y un mtodo que usa un arreglo de parmetros. using System; namespace System { public class Console { public static void WriteLine(string s) {...} public static void WriteLine(string s, object a) {...} public static void WriteLine(string s, object a, object b) {...} ... public static void WriteLine(string s, params object[] args) {...} } } Modificadores public y static El modificador public lo hemos utilizado anteriormente. Se puede utilizar en la declaracin de cualquier mtodo o variable, y como es de esperar, produce el efecto de que el campo afectado se vuelve pblico, esto es, se puede utilizar desde otras clases using System; class Metodo{ public double Divide( double a, double b ) { return a/b; } } class Principal{ public static void Main() { Metodo m = new Metodo(); Console.WriteLine( m.Divide(8, 2) ); } } Si por ejemplo intentamos declarar el mtodo Divide sin el modificador public, obtendremos un error en tiempo de compilacin. El modificador complementario de public es private, que provoca que el mtodo o dato solo sea accesible desde la clase en la que est declarado. Si no se especifica nada, se toma por defecto el modificador private De esta forma podramos separar las clases Metodo y Principal en dos archivos separados, llamados por ejemplo metodo.cs y principal.cs . Para compilar esto, bastar compilar ambos archivos al mismo tiempo, de forma similar a esto: mcs principal.cs metodo.cs Adems, tampoco es necesario crear una instancia de la clase slo para acceder a un mtodo declarado en ella. Para eso debemos anteponer a la declaracin del mtodo el modificador static. Los mtodos estticos se caracterizan por no necesitar una instancia de la clase para cumplir su funcin, pero como contrapartida, no pueden acceder a datos propios de la clase. using System; class Metodo{ public static double Divide( double a, double b ) { return a/b; } } class Principal{ public static void Main() { Console.WriteLine( Metodo.Divide(8, 2) ); } } Los mtodos estticos se utilizan en multitud de situaciones. Por ejemplo, el mtodo Console.WriteLine() o las funciones de la librera matemtica estndar no son ms que mtodos estticos de sus respectivas clases. Constructores e instancias de una clase Como hemos visto, las instancias de una clase se crean con la sintaxis

nombreclase objeto = new nombreclase( argumentos ); donde nombreclase es el nombre que le hemos dado a la definicin de la clase, argumentos es una lista de argumentos posiblemente vaca y objeto es el nombre que queremos darle a la instancia de la clase. Una vez creada una clase, sus miembros se inicializan a sus valores predeterminados ( cero para valores numricos, cadena vaca para el tipo string, etc. ). La siguiente clase representa un punto sobre el plano, de forma que tiene dos valores pblicos X e Y, y un mtodo que calcula la distancia al origen del punto (mdulo) using System; class Punto{ public double X; public double Y; public double Modulo() { double d; d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada return d; } }

class Principal{ public static void Main() { Punto A = new Punto(); A.X = 1; A.Y = 1; Console.WriteLine("El modulo del punto (1,1) es: {0}", A.Modulo() ); } } Ahora bien, la forma en la que se crea la instancia, es decir, inicializando los datos a cero (ejercicio: comprobar esto), se puede personalizar, de forma que podemos construir nuestro propio constructor que le diga a la clase los valores por defecto que debe tomar. Esto se realiza simplemente escribiendo dentro de la clase un mtodo que tenga el mismo nombre que la clase y en el que no se especifica el valor devuelto. La clase Punto con un constructor sera as: using System; class Punto{ public double X; public double Y; public Punto() //constructor { X = 1; Y = 1; } public double Modulo() { double d; d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada return d; } } de forma que ahora al crear una instancia de la clase se crea el punto (1,1) en lugar del (0,0), que era el que se creaba por defecto. De esta forma, al crear la instancia, par ya contendr los valores (1,1) . En la prctica se utilizan mucho constructores con parmetos, de forma que al crear la instancia se le asignan valores segn los parmetros. La siguiente implementacin de Par contiene un constructor que acepta un par de valores, que servirn para inicializar los valores A y B class Punto{ public Punto( double val1, double val2) { X = val1; Y = val2; }

... } Tambin tenemos la posibilidad de declarar una clase con varios constructores (cada uno con diferentes parmetros) Lo que har el compilador de C# es buscar el constructor que se adece a los parmetros que le llegan, y ejecutarlo como si fuera un mtodo ms. Dependiendo de la llamada que se haga en el "new", usaremos un constructor u otro. Sobrecarga de mtodos En C#, al igual que en C++ y en Java es posible definir varios mtodos con el mismo nombre pero con distintos parmetros, de forma que el compilador decide a cul se llama dependiendo de los parmetros que le lleguen. Esto es muy prctico, pues no tienes que renombrar cada funcin segn el tipo de valor que acepta. El siguiente ejemplo implementa un par de mtodos que elevan al cuadrado el valor que reciben, y se implementan para tipos double y para int. En C, qu e es un lenguaje que no soporta sobrecarga de mtodos, se tendra que haber llamado distinto a ambos mtodos, por ejemplo alcuadrado_double y alcuadrado_int using System; class Eleva{ public static double AlCuadrado( int a ) { return a*a; } public static double AlCuadrado( double a ) { return a*a; } } class Principal{ public static void Main() { Console.WriteLine("4 al cuadrado es {0}", Eleva.AlCuadrado(4) ); Console.WriteLine("3.2 al cuadrado es {0}", Eleva.AlCuadrado(3.2) ); } } La palabra reservada this La palabra reservada this sirve para hacer referencia a miembros de la clase en caso de que se quiera especificar, ya sea por motivos de colisin de nombres o por la claridad del cdigo. Su sintaxis es this.campo donde campo es la variable de la clase a la que queremos hacer referencia. En el siguiente ejemplo, declaramos un constructor para la clase Punto, que toma dos argumentos X e Y. Entonces es obligado el uso de this para distinguir entre el X de la clase y el X tomado como parmetro class Complejo { double X; double Y; Complejo(double X, double Y) { this.X = X; this.Y = Y; } } Propiedades e indizadores Propiedades Las propiedades son una caracterstica de C# que permiten aparentemente el acceso a un miembro de la clase mientras mantiene el control asociado al acceso mediante mtodos. Para los programadores de Java hay que decir que esto no es ms que la formalizacin del patrn de asignacin (setter) y mtodo de lectura (getter) Las propiedades son como mtodos que se declaran dentro de un bloque asociado a una variable mediante las palabras reservadas get (se encarga de devolver algo cuando se llama al tipo que lo contiene ) y set (que hace algo cuando se le asigna un valor a la variable que lo contiene. Este valor viene especificado en la variable value ) using System; class TestProperties { private static string clave; public string Clave { get

{ Console.WriteLine ("Acceso a la propiedad clave"); return clave; } set { Console.WriteLine ("Cambio del valor de clave"); clave = value; } } } class Test { public static void Main () { TestProperties tp = new TestProperties(); string c = "ClaveClave"; tp.Clave = c; Console.WriteLine (tp.Clave); } } En realidad, lo que se hace es declarar una variable privada de forma que no se puede acceder de forma directa, y se crean dos mtodos ( o uno si solo se requiere acceso de lectura) que permiten acceder al contenido de la variable y tal vez modificarla. Si no queremos que se pueda modificar la variable, no inclumos el mtodo "set" y ya tendramos propiedades de slo lectura. Indexadores Hemos visto, en el apartado en el que tratamos las propiedades, que podemos acceder a una variable privada de una clase a travs de eventos que nos permiten controlar la forma en la que accedemos a dicha variable. Los indexadores nos van a permitir hacer algo parecido. Nos van a permitir acceder a una clase como si se tratara de un arreglo. Lo vemos de forma ms sencilla con un ejemplo: using System; class PruebaIndexadores { private int[] tabla = {1, 2, 3, 4}; public int this [int indice] { get { Console.WriteLine ("La posicion {0} de la tabla tiene el valor {1}", indice, tabla[indice]); return tabla[indice]; } set { Console.WriteLine ("Escrito el valor {0} en la posicin {1} de la tabla", value, indice); tabla[indice] = value; } } } Tenemos una clase PruebaIndexadores en la que hay un array llamado "tabla", declarado como privado, por lo que no podremos acceder a l desde fuera de nuestra clase. Pero hemos declarado tambin un indexador (public int this [int indice]), que nos permitir acceder a l de forma ms controlada. Para probar esta clase, creamos otra clase con un punto de entrada (public static void Main ()), que ser donde hagamos las pruebas. Primero creamos un objeto de la clase PruebaIndexadores: PruebaIndexadores obj = new PruebaIndexadores (); Luego accedemos a una posicin del indexador: int a = obj[3]; Esta lnea lo que hace es llamar al indexador, pasndole como parmetro el ndice, en este caso 3. Al ser una consulta de lectura, se ejecuta el cdigo que haya en la parte "get" del indexador. Una vez ejecutado, lo que nos aparece por pantalla es esto: La posicion 3 de la tabla tiene el valor 4 Vamos ahora a hacer un cambio en la tabla: obj[3] = 6;

Lo que se ejecuta ahora es la parte "set" del indexador. Lo que aparecer en pantalla una vez ejecutado esto ser: Escrito el valor 6 en la posicin 3 de la tabla Ntese que tenemos que hacer explcitamente el acceso al array (tabla[indice]=value) en el set, ya que el indexador no tiene forma de saber qu variable se supone que tiene que manejar. Si no pusiramos esa lnea, en realidad el indexador no cambiara el valor del array. Para comprobar que realmente se ha hecho el cambio, volvemos a acceder al indexador: a = obj[3]; Y esta vez nos aparecer esto: La posicion 3 de la tabla tiene el valor 6.

Sobrecarga de operadores
La sobrecarga de operadores en C# permite redefinir la accin de un operador en relacin a una clase. Por ejemplo podemos plantear una clase Vector y luego redefinir el operador + para dicha clase. Luego cuando sumamos dos objetos de esa clase vector podemos generar otro objeto de dicha clase que resulte de la suma de sus componentes. El empleo de la sobrecarga de operadores debe hacerse con mucho cuidado de no desvirtuar el concepto que representa dicho operador (por ejemplo sobrecargar el operador "-" para la clase Vector y que genere la suma de sus componentes) Problema 1: Plantear una clase VectorEnteros que permita crear un vector de 5 elementos y sobrecargue el operador + Programa: using using using using System; System.Collections.Generic; System.Linq; System.Text;

namespace SobreCargaDeOperadores { class Program { static void Main(string[] args) { VectorEnteros v1 = new VectorEnteros(); Console.WriteLine("Carga el Primer vector"); v1.Cargar(); VectorEnteros v2 = new VectorEnteros(); Console.WriteLine("Carga el Segundo vector"); v2.Cargar(); Console.WriteLine("Primer vector"); v1.Imprimir(); Console.WriteLine("segundo vector"); v2.Imprimir(); VectorEnteros vt; vt = v1 + v2; Console.WriteLine("Vector Resultante"); vt.Imprimir(); Console.ReadKey(); } } class VectorEnteros { private int[] vec; public VectorEnteros() { vec = new int[5]; } public void Cargar() {

for (int f = 0; f < vec.Length; f++) { Console.Write("Ingrese componente: "); vec[f] = int.Parse(Console.ReadLine()); } } public void Imprimir() { for (int f = 0; f < vec.Length; f++) { Console.Write(vec[f]+" "); } Console.WriteLine(); } public static VectorEnteros operator +(VectorEnteros v1, VectorEnteros v2) { VectorEnteros su = new VectorEnteros(); for (int f = 0; f < su.vec.Length; f++) { su.vec[f] = v1.vec[f] + v2.vec[f]; } return su; } } } Salida:

La sintaxis para sobrecargar un operador binario es: public static {valor que retorna} operator {operador}(tipo-parametro nombre, tipo-parametro nombre) En nuestro ejemplo el tipo de dato que retorna es un objeto de la clase VectorEnteros. El operador que estamos sobrecargando es el "+" y entre parntesis indicamos los dos parmetros que llegan que son objetos de la clase VectorEnteros: public static VectorEnteros operator +(VectorEnteros v1, VectorEnteros v2) Dentro del mtodo creamos un objeto de la clase VectorEnteros: VectorEnteros su = new VectorEnteros(); Luego mediante un for cargamos cada elemento del vector de enteros con los datos de las componentes homlogas de los otros dos vectores: for (int f = 0; f < su.vec.Length; f++) { su.vec[f] = v1.vec[f] + v2.vec[f]; } Como estamos en la clase VectorEnteros podemos acceder a los atributos privados vec. Finalmente retornamos el objeto de la clase VectorEnteros que acabamos de crear: return su; El mtodo completo queda codificado entonces con la siguiente sintaxis: public static VectorEnteros operator +(VectorEnteros v1, VectorEnteros v2)

{ VectorEnteros su = new VectorEnteros(); for (int f = 0; f < su.vec.Length; f++) { su.vec[f] = v1.vec[f] + v2.vec[f]; } return su; } Luego cuando utilizamos el operador + con dos objetos de la clase VectorEnteros el resultado el otro objeto de la clase VectorEnteros: VectorEnteros vt; vt = v1 + v2; Console.WriteLine("Vector Resultante"); vt.Imprimir(); Como podemos ver no creamos el objeto vt sino la llamada al operador + con dos objetos de la clase VectorEnteros retorna un objeto de la clase VectorEnteros. Problema 2: Plantear una clase VectorEnteros que permita crear un vector de 5 elementos y sobrecargue el operador * de un objeto de la clase VectorEnteros con un valor de tipo int (el resultado debe ser otro objeto de la clase VectorEnteros donde cada componente se obtiene de multiplicar su valor por el valor entero) Programa:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SobrecargaOperadores2 { class VectorEnteros { private int[] vec; public VectorEnteros() { vec = new int[5]; } public void Cargar() { for (int f = 0; f < vec.Length; f++) { Console.Write("Ingrese componente:"); vec[f] = int.Parse(Console.ReadLine()); } } public void Imprimir() { for (int f = 0; f < vec.Length; f++) { Console.Write(vec[f] + " "); } Console.WriteLine(); } public static VectorEnteros operator *(VectorEnteros v1, int valor) { VectorEnteros resu = new VectorEnteros(); for (int f = 0; f < resu.vec.Length; f++) { resu.vec[f] = v1.vec[f] * valor; } return resu; } }

class Program { static void Main(string[] args) { VectorEnteros v1 = new VectorEnteros(); Console.WriteLine("Carga del vector"); v1.Cargar(); VectorEnteros vr; Console.WriteLine("Primer Vector"); v1.Imprimir(); vr = v1 * 10; Console.WriteLine("Vector resultante"); vr.Imprimir(); Console.ReadKey(); } } } Como vemos ahora estamos sobrecargando el operador "*". El mtodo tiene dos parmetros uno de tipo VectorEnteros y otro de tipo int: public static VectorEnteros operator *(VectorEnteros v1, int valor) { VectorEnteros resu = new VectorEnteros(); for (int f = 0; f < resu.vec.Length; f++) { resu.vec[f] = v1.vec[f] * valor; } return resu; } No lo hemos hecho pero podramos tambin sobrecargar el operador "*" y recibir como parmetro dos objetos de la clase VectorEnteros: public static VectorEnteros operator *(VectorEnteros v1, VectorEnteros v2) { VectorEnteros resu = new VectorEnteros(); for (int f = 0; f < resu.vec.Length; f++) { resu.vec[f] = v1.vec[f] * v2.vec[f]; } return resu; } Operacin unaria Los ejemplos anteriores mostraban la sobrecarga de operadores binarios (un operador y dos operandos), un operador unario afecta solo un operado. Problema 3: Sobrecargar el operador ++ en la clase VectorEnteros (se debe incrementar en uno cada elemento) Programa:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SobrecargaOperadores3 { class VectorEnteros { private int[] vec; public VectorEnteros() { vec = new int[5]; } public void Cargar() { for (int f = 0; f < vec.Length; f++) {

Console.Write("Ingrese componente:"); vec[f] = int.Parse(Console.ReadLine()); } } public void Imprimir() { for (int f = 0; f < vec.Length; f++) { Console.Write(vec[f] + " "); } Console.WriteLine(); } public static VectorEnteros operator ++(VectorEnteros v) { VectorEnteros resu = new VectorEnteros(); for (int f = 0; f < v.vec.Length; f++) { resu.vec[f]=v.vec[f]+1; } return resu; } } class Program { static void Main(string[] args) { VectorEnteros v1 = new VectorEnteros(); Console.WriteLine("Carga del vector"); v1.Cargar(); Console.WriteLine("Impresin del vector"); v1.Imprimir(); v1++; Console.WriteLine("Impresin del vector luego del operador ++"); v1.Imprimir(); Console.ReadKey(); } } } Cuando se sobrecarga un operador unario tenemos un solo parmetro: public static VectorEnteros operator ++(VectorEnteros v) { VectorEnteros resu = new VectorEnteros(); for (int f = 0; f < v.vec.Length; f++) { resu.vec[f]=v.vec[f]+1; } return resu; } Para ejecutar el operador luego desde la Main: VectorEnteros v1 = new VectorEnteros(); Console.WriteLine("Carga del vector"); v1.Cargar(); Console.WriteLine("Impresin del vector"); v1.Imprimir(); v1++; Sobrecarga de operadores relacionales. Los operadores relacionales devuelven un valor de tipo bool. Cuando se sobrecargan los operadores relacionales estamos obligados a implementar en pares, es decir si emplementamos el == debemos implementar el != en forma obligatoria (sino se genera un error sintctico. Los pares son: == != < > <=

>= Problema 4: Sobrecargar el operador == en la clase VectorEnteros (retornar true si los cinco enteros son iguales) Programa: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SobrecargaOperadores4 { class VectorEnteros { private int[] vec; public VectorEnteros() { vec = new int[5]; } public void Cargar() { for (int f = 0; f < vec.Length; f++) { Console.Write("Ingrese componente:"); vec[f] = int.Parse(Console.ReadLine()); } } public void Imprimir() { for (int f = 0; f < vec.Length; f++) { Console.Write(vec[f] + " "); } Console.WriteLine(); } public static bool operator ==(VectorEnteros v1, VectorEnteros v2) { for (int f = 0; f < v1.vec.Length; f++) { if (v1.vec[f] != v2.vec[f]) return false; } return true; } public static bool operator !=(VectorEnteros v1, VectorEnteros v2) { for (int f = 0; f < v1.vec.Length; f++) { if (v1.vec[f] == v2.vec[f]) return false; } return true; } } class Program { static void Main(string[] args) { VectorEnteros v1 = new VectorEnteros(); Console.WriteLine("Carga del primer vector"); v1.Cargar();

VectorEnteros v2 = new VectorEnteros(); Console.WriteLine("Carga del segundo vector"); v2.Cargar(); if (v1 == v2) Console.Write("Todos los elementos son iguales"); else Console.Write("No todos los elementos son iguales"); Console.ReadKey(); } } }

Indizadores
Los indizadores permiten acceder mediante subndices a un objeto de una clase que lo implementa. El objetivo es facilitar la implementacin de un algoritmo. El indizador evita que implementemos una serie de mtodos para administrar el objeto. La sintaxis de un indizador es: tipo this[tipo indice] { get { valor que retorna segn indice } set { fijar el valor segn indice } } Problema 1: Confeccionar una clase llamada Cliente definir las propiedades Nombre y Dinero (en el constructor inicializar dichas propiedades) Desarrollar otra clase llamada Banco donde debemos definir y crear un vector de tres elementos de tipo Cliente. Implementar un indizador en la clase Banco que permita acceder a cada cliente por un subndice entero. Programa: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Indizador2 { class Cliente { private string nombre; public string Nombre { set { nombre = value; } get { return nombre; } } private int dinero; public int Dinero { set { dinero = value; } get { return dinero; }

} public Cliente(string nom, int din) { Nombre = nom; Dinero = din; } } class Banco { private Cliente[] clientes; public Banco() { clientes = new Cliente[3]; } public Cliente this[int indice] { set { clientes[indice] = value; } get { return clientes[indice]; } } }

class Program { static void Main(string[] args) { Banco banco1 = new Banco(); Cliente cli1 = new Cliente("juan", 1000); Cliente cli2 = new Cliente("ana", 2000); Cliente cli3 = new Cliente("luis", 1500); banco1[0] = cli1; banco1[1] = cli2; banco1[2] = cli3; Console.WriteLine("Datos de los clientes."); Console.WriteLine(banco1[0].Nombre); Console.WriteLine(banco1[0].Dinero); Console.WriteLine(); Console.WriteLine(banco1[1].Nombre); Console.WriteLine(banco1[1].Dinero); Console.WriteLine(); Console.WriteLine(banco1[2].Nombre); Console.WriteLine(banco1[2].Dinero); Console.WriteLine(); Console.ReadKey(); } } } Problema 2: Implementar una clase llamada Tablero del juego de la Batalla Naval. Permitir mediante un indizador de dos dimensiones acceder a las casillas del tablero. Programa: using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace Indizador3 { public enum DatoCasilla { agua, barco }; class Tablero { private DatoCasilla [,] mat; public Tablero() { mat = new DatoCasilla[10, 10]; } public void Graficar() { for (int f = 0; f < mat.GetLength(0); f++) { for (int c = 0; c < mat.GetLength(1); c++) { if (mat[f, c] == DatoCasilla.agua) { Console.Write("0"); } if (mat[f, c] == DatoCasilla.barco) { Console.Write("-"); } } Console.WriteLine(); } } public DatoCasilla this[int fila, int columna] { set { mat[fila, columna] = value; } get { return mat[fila, columna]; } } } class Program { static void Main(string[] args) { Tablero tablero1 = new Tablero(); tablero1[0, 0] = DatoCasilla.barco; tablero1[0, 1] = DatoCasilla.barco; tablero1[0, 2] = DatoCasilla.barco; tablero1[0, 9] = DatoCasilla.barco; tablero1[1, 9] = DatoCasilla.barco; tablero1[2, 9] = DatoCasilla.barco; tablero1.Graficar(); Console.WriteLine(); if (tablero1[0, 0] == DatoCasilla.barco) { Console.WriteLine("Hay un barco en esta casilla"); } else { if (tablero1[0, 0] == DatoCasilla.agua) { Console.WriteLine("Agua"); }

} Console.ReadKey(); } } }

Mtodos estticos En C# podemos definir mtodos que se crean independientemente a la definicin de objetos. Un mtodo esttico puede llamarse sin tener que crear un objeto de dicha clase. Un mtodo esttico tiene ciertas restricciones:

No puede acceder a los atributos de la clase (salvo que sean estticos) No puede utilizar el operador this, ya que este mtodo se puede llamar sin tener que crear un objeto de la clase. Puede llamar a otro mtodo siempre y cuando sea esttico. Un mtodo esttico es lo ms parecido a lo que son las funciones en los lenguajes estructurados (con la diferencia que se encuentra encapsulado en una clase)

Si recordamos cada vez que creamos un programa en C# debemos especificar el mtodo Main: static void Main(string[] args) El mtodo Main es esttico para que el sistema operativo pueda llamarlo directamente sin tener que crear un objeto de la clase que lo contiene.

Problema 1:
Implementar una clase llamada Operacion. Definir dos mtodos estticos que permitan sumar y restar dos valores enteros. Programa:
using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace MetodosEstaticos1 { class Operacion { public static int Sumar(int x1, int x2) { int s = x1+x2; return s;

public static int Restar(int x1,int x2) { int r = x1 - x2; return r; } }

class Program { static void Main(string[] args) { Console.Write("La suma de 2+4 es "); Console.WriteLine(Operacion.Sumar(2, 4)); Console.Write("La resta de 6-2 es "); Console.WriteLine(Operacion.Restar(6, 2)); Console.ReadKey(); } } }

Agregamos la palabra clave static antes de indicar el valor que retorna el mtodo: public static int Sumar(int x1, int x2) { int s = x1+x2; return s; } Luego cuando llamamos al mtodo esttico lo hacemos antecediendo el nombre de la clase: Console.Write("La suma de 2+4 es "); Console.WriteLine(Operacion.Sumar(2, 4)); Console.Write("La resta de 6-2 es "); Console.WriteLine(Operacion.Restar(6, 2)); Es importante notar que no se crean objetos de la clase Operacion para poder llamar a los mtodos Sumar y Restar.

Problema propuesto

1. Plantear una clase llamada VectorEnteros que defina tres mtodos estticos. El primero retorna el mayor elemento del vector, el segundo el menor elemento y el tercero la suma de sus componentes.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MetodosEstaticos2 { class VectorEnteros { public static int Mayor(int[] vec) { int may = vec[0]; for (int x = 0; x < vec.Length; x++) { if (vec[x] > may) { may = vec[x]; } } return may; } public static int Menor(int[] vec) { int men = vec[0]; for (int x = 0; x < vec.Length; x++) { if (vec[x] < men) { men = vec[x]; } } return men; } public static int SumaElementos(int[] vec) { int suma = 0; for (int x = 0; x < vec.Length; x++) { suma = suma + vec[x]; } return suma; } public static void Imprimir(int[] vec) { Console.Write("Impresin del vector:"); for (int x = 0; x < vec.Length; x++) { Console.Write(vec[x] + " "); ; } Console.WriteLine(); } } class Program { static void Main(string[] args) { int[] v = { 5, 6, 20 }; VectorEnteros.Imprimir(v); Console.WriteLine("El mayor elemento del vector es :" + VectorEnteros.Mayor(v)); Console.WriteLine("El menor elemento del vector es :" + VectorEnteros.Menor(v));

Console.Write("La suma de los elementos del vector es :" + VectorEnteros.SumaElementos(v)); Console.ReadKey(); } } }

MTODOS
Ya dijimos en la introduccin a la POO que los mtodos son todos aquellos bloques de cdigo que se ocupan de manejar los datos de la clase. Recapitulemos un momento y echemos un nuevo vistazo al ejemplo del coche que pusimos en la introduccin. En l tenamos tres mtodos: Acelerar, Girar y Frenar, que servan para modificar la velocidad y la direccin de los objetos de la clase coche. Como ves, los mtodos sirven para que los objetos puedan ejecutar una serie de acciones. Veamos cmo se define un mtodo en C#:

acceso tipo NombreMetodo(TipoArg1 arguento1, TipoArg2 arguento2 ...) { // Aqu se codifica lo que tiene que hacer el mtodo }

Veamos: acceso es el modificador de acceso del mtodo, que puede ser private, protected, internal o public (como las variables). Posteriormente el tipo de retorno, es decir, el tipo de dato que devolver el mtodo (que puede ser cualquier tipo). Luego el nombre del mtodo (sin espacios en blanco ni cosas raras). Despus, entre parntesis y separados unos de otros por comas, la lista de argumentos que aceptar el mtodo: cada uno de ellos se especificar poniendo primero el tipo y despus el nombre del mismo. Por fin, la llave de apertura de bloque seguida del cdigo del mtodo y, para terminarlo, la llave de cierre del bloque.

Vamos a ilustrar esto con un ejemplo: vamos a construir una clase Bolgrafo; sus mtodos sern, por ejemplo, Pintar y Recargar, que son las operaciones que se suelen efectuar con un bolgrafo. Ambos mtodos modificarn la cantidad de tinta del boli, valor que podramos poner en una propiedad llamada Tinta, por ejemplo. Para aquellos que conozcis la programacin procedimental, un mtodo es como un procedimiento o una funcin. En determinadas ocasiones necesitaremos pasarle datos a los mtodos para que estos puedan hacer su trabajo. Por ejemplo, siguiendo con el bolgrafo, puede que necesitemos decirle al mtodo Pintar la cantidad de tinta que vamos a gastar, igual que hacamos con el mtodo Acelerar de la clase Coche, que tenamos que decirle cunto queramos acelerar. Pues bien, estos datos se llaman argumentos. Vamos a verlo:

using System;
class

Boligrafo color=0;

{
protected int

protected

byte tinta=100; Pintar(byte gasto)


return false;

public bool

{
if

(gasto>this.tinta)

this.tinta

-= gasto; Console.WriteLine("Se gastaron {0} unidades de tinta.", gasto); return true;

}
public void

Recargar()

{
this.tinta=100;

Console.WriteLine("Bolgrafo recargado"); }
public int

Color

{
get

{
return this.color;

}
set

{
this.color

= value;

} }
public

byte Tinta

{
get

{
return this.tinta;

} } }

De momento fjate bien en lo que conoces y en lo que estamos explicando, que son los mtodos. Lo dems lo iremos conociendo a su debido tiempo. En este ejemplo tienes los mtodos Pintar y Recargar (presta especial atencin a la sintaxis). El primero disminuye la cantidad de tinta, y el segundo establece esta cantidad nuevamente a 100, es decir, rellena el bolgrafo de tinta.

Los mtodos tambin pueden devolver un valor despus de su ejecucin si fuera necesario. En este ejemplo, el mtodo Pintar devuelve True si la operacin se ha podido efectuar y False si no se ha podido (fjate en que el tipo de retorno es bool). De este modo, el cliente simplemente debera fijarse en el valor devuelto por el mtodo para saber si todo ha funcionado correctamente, sin tener que comparar los datos de antes con los de despus (es decir, sin comprobar si el

valor de la propiedad tinta, en este caso, se ha visto modificado). Este mtodo Main que vamos a poner a continuacin demostrar el funcionamiento de la clase Bolgrafo:

class

BoligrafoApp

{ static void Main() { // Instanciacin del objeto Boligrafo boli = new Boligrafo(); Console.WriteLine("El boli tiene {0} unidades de tinta", boli.Tinta); Console.WriteLine("boli.Pintar(50) devuelve {0}", boli.Pintar(50)); Console.WriteLine("Al boli le quedan {0} unidades de tinta", boli.Tinta); Console.WriteLine("boli.Pintar(60) devuelve {0}", boli.Pintar(60)); Console.WriteLine("Al boli le quedan {0} unidades de tinta", boli.Tinta); boli.Recargar(); Console.WriteLine("Al boli le quedan {0} unidades de tinta", boli.Tinta); string a = Console.ReadLine(); } }

Bien, la salida en consola de este programa sera la siguiente:

El boli tiene 100 unidades de tinta Se gastaron 50 unidades de tinta. boli.Pintar(50) devuelve True Al boli le quedan 50 unidades de tinta boli.Pintar(60) devuelve False Al boli le quedan 50 unidades de tinta Bolgrafo recargado Al boli le quedan 100 unidades de tinta

Examinemos el cdigo y el resultado un momento. En primer lugar, como ves, instanciamos el objeto boli con el operador new y escribimos la cantidad de tinta del mismo en la consola. Efectivamente, Tinta vale 100 porque la variable protected que almacena este valor (la variable tinta) est inicializada a 100 en la declaracin. A continuacin, en el mtodo Main, se pretende escribir lo que devuelva el mtodo Pintar. Sin embargo, como ves, antes de eso aparece en la consola otra lnea, la que escribe precisamente este mtodo (Pintar). Por qu sale primero esto y despus lo que est escrito en el mtodo Main? Pues hombre, para que el mtodo devuelva algo se tiene que haber ejecutado primero. Lgico, no? Bien, como ves, la primera llamada al mtodo Pintar devuelve True porque haba tinta suficiente para hacerlo. Despus se escribe la tinta que queda y se vuelve a llamar al mtodo Pintar, pero esta vez le pasamos como argumento un nmero mayor que la tinta que quedaba. Por este motivo, ahora el mtodo pintar devuelve False y no escribe nada en la consola. Posteriormente se ejecuta el mtodo Recargar, que no devuelve nada y escribe "Bolgrafo recargado" en la consola, y,

por ltimo, se vuelve a escribir la cantidad de tinta, que vuelve a ser 100. De todo esto podemos extraer dos ideas principales con las que quiero que te quedes de momento: una es que los mtodos pueden devolver un valor de cualquier tipo, y la otra es que si un mtodo no devuelve nada hay que declararlo de tipo void.

Veamos todo esto con otro ejemplo. Vamos a escribir una clase (muy simplificada, eso s) que se ocupe de manejar gastos e ingresos, sin intereses ni nada:

class Cuentas { protected double saldo=0; public double Saldo { get { return this.saldo; } } public bool NuevoGasto(double cantidad) { if (cantidad<=0) return false; this.saldo -= cantidad; return true; } public bool NuevoIngreso(double cantidad) { if (cantidad <=0) return false; this.saldo += cantidad; return true; } }

En esta clase hay una variable protected (o sea, que es visible dentro de la clase y dentro de clases derivadas, pero no desde el cliente), una propiedad y dos mtodos. Como te dije antes, presta especial atencin a lo que conoces y, sobre todo, a los mtodos, que es con lo que estamos. Los mtodos NuevoIngreso y NuevoGasto se ocupan de modificar el valor de la variable saldo segn cunto se ingrese o se gaste. Ahora bien, si la cantidad que se pretende ingresar es menor o igual que cero, el mtodo no modificar el valor de la variable saldo y devolver false. Quiero que te fijes de nuevo en cmo se declara un mtodo: en primer lugar el modificador de acceso (que puede ser public, protected, private o internal), despus el tipo de dato que retornar, que podr ser cualquier tipo de dato ( y en caso de que el mtodo no devuelva ningn dato, hay que poner void), despus el nombre del mtodo y, por ltimo, la lista de argumentos entre parntesis. Ya s que me estoy repitiendo, pero es que esto es muy importante.

Sobrecarga de mtodos
La sobrecarga de mtodos consiste en poner varios mtodos con el mismo nombre en la misma clase, pero siempre que su lista de argumentos sea distinta. Ojo, repito, siempre que su lista de argumentos sea distinta, es decir, no puede haber dos mtodos que se llamen igual con la misma lista de argumentos, aunque devuelvan datos de distinto tipo. El compilador sabra a cul de todas las sobrecargas nos referimos por los argumentos que se le pasen en la llamada, pero no sera capaz de determinar cul de ellas debe ejecutar si tienen la misma lista de argumentos . Por ejemplo, no podramos sobrecargar el mtodo NuevoIngreso de este modo:

public int NuevoIngreso(double cantidad) //Error. No se puede sobrecargar as {...}

A pesar de devolver un valor int en lugar de un bool, su lista de argumentos es idntica, por lo que el compilador avisara de un error. Sin embargo, s podramos sobrecargalo de estos modos:

public bool NuevoIngreso(single cant) {...} public int NuevoIngreso(double cantidad, double argumento2) {...} public int NuevoIngreso(single cantidad, double argumento2) {...}

Cada sobrecarga tiene marcado en negrilla el elemento que la hace diferente de las dems. Y as hasta hartarnos de aadir sobrecargas. Hay un detalle que tambin es importante y que no quiero pasar por alto: lo que diferencia las listas de argumentos de las diferentes sobrecargas no es el nombre de las variables, sino el tipo de cada una de ellas. Por ejemplo, la siguiente sobrecarga tampoco sera vlida:

public bool NuevoIngreso(double num) //Error. No se puede sobrecargar as {...}

A pesar de que el argumento tiene un nombre distinto (num en lugar de cantidad), es del mismo tipo que el del mtodo del ejemplo, por lo que el compilador tampoco sabra cul de las dos sobrecargas ejecutar.

Bueno, supongo que ahora vendr la pregunta: Cul de todas las sobrecargas vlidas ejecutar si efecto la siguiente llamada?

MisCuentas.NuevoIngreso(200.53);

Efectivamente, aqu podra haber dudas, ya que el nmero 200.53 puede ser tanto double, como single. Para nmeros decimales, el compilador ejecutar la sobrecarga con el argumento de tipo double. En el caso de nmeros enteros, el compilador ejecutar la sobrecarga cuyo argumento mejor se adapte con el menor consumo de recursos (int, uint, long y unlong, por este orden). Y ahora vendr la otra pregunta: y si yo quiero que, a pesar de todo, se ejecute la sobrecarga con el argumento de tipo single? Bien, en ese caso tendramos que aadir un sufijo al nmero para indicarle al compilador cul es el tipo de dato que debe aplicar para el argumento:

MisCuentas.NuevoIngreso(200.53F);

Los sufijos para literales de los distintos tipos de datos numricos son los siguientes:

L (mayscula o minscula): long ulong, por este orden; U (mayscula o minscula): int uint, por este orden; UL LU (independientemente de que est en maysuculas o minsculas): ulong; F (mayscula o minscula): single; D (mayscula o minscula): double; M (mayscula o minscula): decimal;

Argumentos pasados por valor y por referencia


Puede que necesitemos que los mtodos NuevoIngreso y NuevoGasto devuelvan el saldo nuevo, adems de true o false. Podemos hacerlo? Veamos: siendo estrictos en la respuesta, no se puede, ya que un mtodo no puede retornar ms de un valor. Sin embargo, s podemos hacer que un mtodo devuelva datos en uno o varios de sus argumentos. Cmo? Pues pasando esos argumentos por referencia. Me explicar mejor: un mtodo puede aceptar argumentos de

dos formas distintas (en C# son tres, aunque dos de ellas tienen mucho que ver): argumentos pasados por valor y argumentos pasados por referencia.

Cuando un mtodo recibe un argumento por valor, lo que ocurre es que se crea una copia local de la variable que se ha pasado en una nueva direccin de memoria. As, si el mtodo modifica ese valor, la modificacin se hace en la nueva direccin de memoria, quedando la variable original sin cambio alguno. Por ejemplo, si hubiramos escrito el mtodo NuevoIngreso de este modo:

public bool NuevoIngreso(double cantidad) { if (cantidad <=0) return false; this.saldo += cantidad; cantidad=this.saldo; return true; }

Si el saldo era 100, y efectuamos la siguiente llamada, cul sera la salida en la consola?:

double dinero=345.67; MisCuentas.NuevoIngreso(dinero); Console.Write(dinero);

Eres programador de Visual Basic? Pues te has equivocado. La salida sera 345.67, es decir, la variable dinero no ha sido modificada, ya que se ha pasado al mtodo por valor (en C#, si no se indica otra cosa, los argumentos de los mtodos se pasan por valor). Veamos qu es lo que ha ocurrido:

La variable dinero apunta a una determinada zona de memoria. Al pasarse esta variable por valor, el compilador hace una copia de este dato en otra zona de memoria a la que apunta la variable cantidad. As, cuando se modifica el valor de esta, se modifica en esta nueva zona de memoria, quedando intacta la zona de memoria asignada a la variable dinero.

Sin embargo, si escribimos el mtodo del siguiente modo para que reciba los valores por referencia:

public bool NuevoIngreso(ref double cantidad) { if (cantidad <=0) return false; this.saldo += cantidad; cantidad=this.saldo; return true; }

Y modificamos tambin el cdigo que haca la llamada:

double dinero=345.67; MisCuentas.NuevoIngreso(ref dinero); Console.Write(dinero);

La salida en la consola sera 445.67. Veamos dnde est la diferencia:

Fjate bien en que, ahora, la variable cantidad apunta a la misma zona de memoria a la que apunta la variable dinero. Por este motivo, cualquier modificacin que se haga sobre la variable cantidad afectar tambin a la variable dinero, ya que dichas modificaciones se harn en la zona de memoria reservada para ambas.

Sin embargo, las variables que se pasen a un mtodo usando ref deben de haber sido inicializadas previamente, es decir, el programa no se habra compilado si no se hubiera inicializado la variable dinero. Si queremos pasar por referencia argumentos cuyo valor inicial no nos interesa deberamos usar out en lugar de ref. Por ejemplo, imagina que queremos devolver en otro argumento el valor del saldo redondeado. Para qu? No s, hombre, slo es un ejemplo... Habra que hacerlo as:

public bool NuevoIngreso(ref double cantidad, out int redondeado) { redondeado=(int) Math.Round(this.saldo); if (cantidad <=0) return false; this.saldo += cantidad; cantidad=this.saldo; redondeado=(int) Math.Round(this.saldo); return true; }

Y modificamos tambin el cdigo que haca la llamada:

double dinero=345.67; int redoneo; MisCuentas.NuevoIngreso(ref dinero, out redondeo); Console.Write(redondeo);

Ahora la salida en la consola sera 346. Fjate que la variable redondeo no ha sido inicializada antes de efectuar la llamada al mtodo (no ha recibido ningn valor). Por otro lado, este argumento debe recibir algn valor antes de que el mtodo retorne, por lo que se asigna antes del if y luego se asigna otra vez despus de hacer el ingreso. Sin embargo, la variable dinero s ha sido inicializada antes de invocar el mtodo, puesto que el mtodo necesitaba saber cunto haba que ingresar, pero no necesita saber nada del valor redondeado, ya que este se calcular a partir del saldo.

Mtodos static
En efecto, por fin vas a saber qu significaba eso de que el mtodo Main tena que ser static. Bien, los mtodos static, son aquellos que se pueden ejecutar sin necesidad de instanciar la clase donde est escrito. La definicin de Tom Archer en el captulo 6 de su libro "A fondo C#" me parece excelente; dice as: " Un mtodo esttico es un mtodo que existe en una clase como un todo ms que en una instancia especfica de la clase". Mucho mejor, verdad? Por lo tanto, el hecho de que el mtodo Main tenga que ser static no es un capricho, ya que, de lo contrario, el CLR no sera capaz de

encontrarlo pues antes de que se ejecute la aplicacin, lgicamente, no puede haber instancias de ninguna de las clases que la componen.

Estos mtodos suelen usarse para hacer una serie de operaciones globales que tienen mucho ms que ver con la clase como tal que con una instancia especfica de la misma: por ejemplo, si tenemos una clase Coche y queremos listar todas las marcas de coches de que disponemos, lo ms propio es un mtodo static. Qu necesidad tenemos de instanciar un objeto de esa clase, si solamente queremos ver las marcas disponibles? Otro ejemplo podra ser un mtodo static en la clase Bolgrafo que devolviera una cadena con el nombre del color que le corresponde a un determinado nmero, ya que no necesitara instanciar un objeto de la clase para saber si al nmero uno le corresponde el color negro, o al 5 el rojo, por ejemplo. Por lo tanto, los mtodos static no aparecen como miembros de las instancias de una clase, sino como parte integrante de la propia clase. Vamos a poner un pequeo programa completo que ilustre el uso de los mtodos static:

using

System; VisaElectron VisaElectron


public static ushort

namespace

{
class

{ Limite() {
return

300;

}
// Aqu iran ms miembros de la clase

}
class

VisaElectronApp
static void

{ Main() { Console.WriteLine("El lmite de la Visa electrn es: {0}", VisaElectron.Limite());


string

a=Console.ReadLine();

} } }

Para hacer que un mtodo sea static hay que poner esta palabra despus del modificador de acceso (si lo hay) y antes del tipo de retorno del mtodo. Este mtodo devolvera el lmite que tienen todas las tarjetas Visa Electrn para extraer dinero en un slo da, (que no s cul es, pero lmite tienen). Ahora presta especial atencin a cmo se invoca este

mtodo dentro del mtodo Main (est en negrilla). En efecto, no se ha instanciado ningn objeto de la clase VisaElectron, sino que se ha puesto directamente el nombre de la clase.

Por otro lado, soy consciente de que este no es el mejor diseo para esta clase en concreto, pero mi inters principal ahora es que veas muy claro cmo se define un mtodo static y cmo se invoca. Ms adelante, cuando empecemos con la herencia, trataremos la redefinicin de mtodos y el polimorfismo. Por hoy, creo que tenemos suficiente.

TransactionScope - Simplificando el trabajo con transacciones.


A partir del Framewrok 2.0 tenemos a nuestra disposicin el objeto TransactionScope, que permite simplificar el trabajo con transacciones. Especialmente llamativo es la capacidad de promocionar la transaccin a un entorno de transacciones distribuidas (MS DTC - COM+) de forma casi transparente . Vamos a empezar con un ejemplo sencillito, de esos que no tienen que fallar nunca. Lo primero que debemos hacer es aadir una referencia a System.Transactions en nuestro proyecto. Sobre una base de datos de prueba, creamos una nueva tabla para nuestro ejemplo.

CREATE TABLE Persona (CodPersona int IDENTITY, Nombre varchar(50), Apellido1 varchar(100), Apellido2 varchar(100), FechaNacimiento datetime, CONSTRAINT PK_Persona PRIMARY KEY (CodPersona) ) Para acceder a los datos vamos a utilizar LinqToSQL, por lo que aadimos un nuevo archivo de este tipo a nuestro proyecto. Por supuesto tambin podramos trabajar con el modelo clsico de ADO.NET. Podemos ver como aadir clases de LinqToSQL a nuestro proyecto desde este enlace:
http://www.devjoker.com/contenidos/Articulos/326/LinQ-To-SQL--Un-ejemplo-sencillo.aspx

Ahora creamos un formulario - con un nico botn - y aadimos el siguiente cdigo al evento click del botn.

using (TransactionScope transactionScope = new TransactionScope()) { try { AddData(); transactionScope.Complete(); MessageBox.Show("OK"); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error - Se deshacen los cambios"); } } Con esto estamos envolviendo el cdigo con un objeto TransactionScope - que crea a su vez una transaccin controlada por el componente de Windows denominado LTM (Lightweight Transaction Coordinator) - es lo que se conoce como ambito o contexto de la transaccin. Posteriormente llamamos a un mtodo que se encarga de grabar los registros en la base de datos y si todo ha ido bien confirmamos las transaccion con el mtodo Complete(). Como vemos todo muy simple. El mtodo AddData es un simple bucle que inserta 100 registros en la base de datos. Lo vemos - no debemos tener ningn tipo de problema si hemos leido el artculo de LinqToSql que hemos mencionado antes.

private void AddData() { using (DataDataContext ctx = new DataDataContext()) { for (int i = 0; i < 100; i++){ Persona p = new Persona();

p.Nombre = String.Format("Nombre {0}", i); p.Apellido1 = String.Format("Apellido1 {0}", i); p.Apellido2 = String.Format("Apellido2 {0}", i); p.FechaNacimiento = DateTime.Now.AddMonths(i * -1); ctx.Personas.InsertOnSubmit(p); } ctx.SubmitChanges(); } } Si consultamos los datos de la tabla Persona - que creamos en nuestra base de datos al principio del artculo -tendremos algo as: CodPersona Nombre Apellido1 Apellido2 FechaNacimiento ----------- ---------- -------------- --------------- ----------------------1 Nombre 0 Apellido1 0 Apellido2 0 2008-07-14 23:26:43.907 2 Nombre 1 Apellido1 1 Apellido2 1 2008-06-14 23:26:43.907 3 Nombre 2 Apellido1 2 Apellido2 2 2008-05-14 23:26:43.907 4 Nombre 3 Apellido1 3 Apellido2 3 2008-04-14 23:26:43.907 5 Nombre 4 Apellido1 4 Apellido2 4 2008-03-14 23:26:43.907 6 Nombre 5 Apellido1 5 Apellido2 5 2008-02-14 23:26:43.907 ... Todo ha ido bien y no hemos debido tener ningn problema. Ahora queremos comprobar que cuando las cosas fallan los cambios se deshacen correctamente. Para ello incluimos un nuevo mtodo, que llamar al mtodo de aadir registros (y que acabamos de ver que funciona correctamente) y una finalizada la insercin de datos lanzar un error..

private void AddDataWithError() { AddData(); throw new ApplicationException("Error provocado"); } Y modificamos nuestro cdigo para que se llame al mtodo que lanza el error.

using (TransactionScope transactionScope = new TransactionScope()) { try { AddDataWithError(); transactionScope.Complete(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error - Se deshacen los cambios"); } } Podemos comprobar que la aplicacin muestra el mensaje de error y revierte correctamente las inserciones. Para ello insertamos un punto de detencin (F9) en la lnea que muestra el MessageBox de error. Cuando la ejecucin de cdgio se pare en el punto de detencin, consultamos la base de datos con la siguiente instruccin:

SELECT * FROM Persona(NOLOCK) En este momento podemos observar que se han inserado 100 nuevos registros, pero la transaccin an no ha sido confirmada - por eso debemos especificar NOLOCK para poder ver los datos. Continuamos la ejecucin del cdigo y volvemos a lanzar la consulta. La transaccin se ha deshecho correctamente y los datos no se ha grabado. Funciona perfectamente. Pero,como se est haciendo commit? donde se est haciendo el Rollback ? Cuando se crea una transaccin utilizandoTrasactionScope, se crea una transaccin ligera que ser controlada por un componente de Windows denominado LTM (Lightweight Transaction Coordinator), es este componente el encargado de controlar la transaccin. Por lo tanto, no es necesario hacer Rollback, una vez que salimos del ambito de la transaccin los cambios se deshacen automanticamente si no han sido confirmados. Es decir, cuando salimos de la clausula using que contiene a TransactionScope. Cuando en la transaccin intervienen ms de un recursos transaccional (diferentes bases de datos, colas ...) la transaccin es promocionada, dejando de ser una transaccin ligera - controladas por el LTM - y se convierte en una transaccin distribuida - coordinada por el MS DTC (Distributed Transaction Coordinator). Es una tcnica realmente potente que abordaremos en posteriores articulos. Como inveniente diremos que el coordinador de transacciones distribuidas en un componente COM+ y que por lo tanto se hace uso de interoperatividad con COM+ (y los problemas que ello puede suponer).

También podría gustarte