jueves, 26 de mayo de 2011
martes, 24 de mayo de 2011
viernes, 20 de mayo de 2011
Convert vs Parse
La diferencia que existe entre Int32.Parse y Convert.Int32 no está en su funcionamiento. Estas clases hacen exactamente lo mismo es el mismo, o sea, transforman un objeto en un Int32, la diferencia estriba en su reacción cuando reciben un valor (null):
En este caso, el Int32.Parse nos devolverá una excepción, notificándonos que está recibiendo un dato (null).
Por otro lado, la clase Convert.Int32 nos convertirá el valor (null) a 0, manejando la excepción.
En este caso, el Int32.Parse nos devolverá una excepción, notificándonos que está recibiendo un dato (null).
Por otro lado, la clase Convert.Int32 nos convertirá el valor (null) a 0, manejando la excepción.
jueves, 19 de mayo de 2011
miércoles, 18 de mayo de 2011
domingo, 15 de mayo de 2011
Lambda Expressions
Desde la primer versión de C# existe la posibilidad de usar delegates, objetos que representan una invocación a un método; en esencia un puntero a función fuertemente tipado. Los delegates son utilizados fundamentalmente como callback en eventos, pero pueden ser pasados a funciones para ser ejecutados por las mismas. De esta forma, permite definir contratos en función a firmas de métodos en lugar de requerir el uso (un poco más fuerte) de interfaces.
La declaración de un delegate define la firma de los métodos que pueden ser aceptados por el mismo. Por ejemplo, un delegate que retorna un entero y recibe un string sería definido así:
delegate int TestDelegate(string param1);
y podría instanciarse de esta manera:
class Test
{
int ParseInt(string param1)
{
return int.Parse(param1);
}
static void Main()
{
Test test = new Test();
TestDelegate del = new TestDelegate(test.ParseInt);
Console.WriteLine(del("12") + 1);
}
}
De ejecutarse, este código mostraría el resultado 13.
En este caso, del mantiene una referencia a test y ParseInt se ejecuta en el contexto de dicha referencia.
Está era la única forma que teníamos disponible con C# 1. Ya en la segunda versión de la especificación, se agregó la alternativa de los anonymous delegates. Usando la nueva sintaxis, aumenta la cantidad de cosas que pueden hacerse con los delegates, ya que no es necesario referir a un método el cual será llamado al invocar el delegate, sino que simplemente se puede definir un bloque de código a ejecutarse.
Para el mismo delegate definido arriba, un ejemplo sería:
class Test2
{
static void Main()
{
TestDelegate del = delegate(string param1) {
return int.Parse(param1);
};
Console.WriteLine(del("12") + 1);
}
}
Una característica interesante de los delegates anónimos, es el hecho de que pueden hacer referencia a cualquier variable del contexto en el que son definidos:
class Test3
{
static void Main()
{
int integer = 0;
TestDelegate del = delegate(string param1) {
return int.Parse(param1) + integer;
};
Console.WriteLine(del("12") + 1);
integer++;
Console.WriteLine(del("12") + 1);
}
}
La salida de este programa es 13 y 14, ya que del hace referencia a la variable integer, la cual es modificada luego de la primer invocación. Lo que se vé aquí, es que el delegate está creando una closure, donde el scope actual define el conjunto de variables que son visibles por el delegate.
Esto le da gran poder a los delegates, ya que les permite ser usados en forma más general y sin escribir demasiado código. Un ejemplo, usando iteración:
int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, delegate(int i) { sum += i; }
Console.WriteLine(sum);
Esto nos lleva a la tercer versión del lenguage C#, donde se definen las lambda expressions, que refinan el concepto de anonymous delegate y utilizan las nuevas capacidades de inferencia de tipos del lenguaje para que la sintaxis sea mucho más limpia. El término lambda expression viene del cálculo lambda, la teoría matemática detrás del desarrollo de los lenguajes funcionales (tema para otro día).
De esta manera, el ejemplo anterior se podría reescribir así:
int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, i => sum += i);
Console.WriteLine(sum);
Básicamente, la nueva sintaxis permite indicar el conjunto de parámetros que recibirá el nuevo delegate, sin necesidad de indicar su tipo (hay algunas excepciones). En el caso de delegates simples que retornan un valor, se puede prescindir de usar el keyword return e indicar simplementa la expresión del resultado.
El tipo de delegate generado por una lambda expressión depende fundamentalmente de cómo se va a aplicar. Veamos un ejemplo:
class Test4
{
delegate void TestDelegate2(int num);
static void Main()
{
Func1(i => i + 1);
Func2(i => i + 1);
}
void Func1(Func f1)
{
Console.WriteLine(f1(1));
}
void Func2(TestDelegate2 f2)
{
Console.WriteLine(f2(1));
}
}
Si bien los tipos de f1 y de f2 son diferentes, viendo que el código definido en la expresión es compatible con el tipo de ambos delegates, el compilador permite que utilicemos el mismo código sin necesidad de hacer nada extraño.
Hay otro feature de las lambda expressions que las hace particularmente interesantes y novedosas con respecto a los delegates. Existe un subconjunto de lambada expressions que pueden ser transformadas automáticamente en árboles de expresión, que pueden ser manipulados por el programador.
La declaración de un delegate define la firma de los métodos que pueden ser aceptados por el mismo. Por ejemplo, un delegate que retorna un entero y recibe un string sería definido así:
delegate int TestDelegate(string param1);
y podría instanciarse de esta manera:
class Test
{
int ParseInt(string param1)
{
return int.Parse(param1);
}
static void Main()
{
Test test = new Test();
TestDelegate del = new TestDelegate(test.ParseInt);
Console.WriteLine(del("12") + 1);
}
}
De ejecutarse, este código mostraría el resultado 13.
En este caso, del mantiene una referencia a test y ParseInt se ejecuta en el contexto de dicha referencia.
Está era la única forma que teníamos disponible con C# 1. Ya en la segunda versión de la especificación, se agregó la alternativa de los anonymous delegates. Usando la nueva sintaxis, aumenta la cantidad de cosas que pueden hacerse con los delegates, ya que no es necesario referir a un método el cual será llamado al invocar el delegate, sino que simplemente se puede definir un bloque de código a ejecutarse.
Para el mismo delegate definido arriba, un ejemplo sería:
class Test2
{
static void Main()
{
TestDelegate del = delegate(string param1) {
return int.Parse(param1);
};
Console.WriteLine(del("12") + 1);
}
}
Una característica interesante de los delegates anónimos, es el hecho de que pueden hacer referencia a cualquier variable del contexto en el que son definidos:
class Test3
{
static void Main()
{
int integer = 0;
TestDelegate del = delegate(string param1) {
return int.Parse(param1) + integer;
};
Console.WriteLine(del("12") + 1);
integer++;
Console.WriteLine(del("12") + 1);
}
}
La salida de este programa es 13 y 14, ya que del hace referencia a la variable integer, la cual es modificada luego de la primer invocación. Lo que se vé aquí, es que el delegate está creando una closure, donde el scope actual define el conjunto de variables que son visibles por el delegate.
Esto le da gran poder a los delegates, ya que les permite ser usados en forma más general y sin escribir demasiado código. Un ejemplo, usando iteración:
int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, delegate(int i) { sum += i; }
Console.WriteLine(sum);
Esto nos lleva a la tercer versión del lenguage C#, donde se definen las lambda expressions, que refinan el concepto de anonymous delegate y utilizan las nuevas capacidades de inferencia de tipos del lenguaje para que la sintaxis sea mucho más limpia. El término lambda expression viene del cálculo lambda, la teoría matemática detrás del desarrollo de los lenguajes funcionales (tema para otro día).
De esta manera, el ejemplo anterior se podría reescribir así:
int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, i => sum += i);
Console.WriteLine(sum);
Básicamente, la nueva sintaxis permite indicar el conjunto de parámetros que recibirá el nuevo delegate, sin necesidad de indicar su tipo (hay algunas excepciones). En el caso de delegates simples que retornan un valor, se puede prescindir de usar el keyword return e indicar simplementa la expresión del resultado.
El tipo de delegate generado por una lambda expressión depende fundamentalmente de cómo se va a aplicar. Veamos un ejemplo:
class Test4
{
delegate void TestDelegate2(int num);
static void Main()
{
Func1(i => i + 1);
Func2(i => i + 1);
}
void Func1(Func
{
Console.WriteLine(f1(1));
}
void Func2(TestDelegate2 f2)
{
Console.WriteLine(f2(1));
}
}
Si bien los tipos de f1 y de f2 son diferentes, viendo que el código definido en la expresión es compatible con el tipo de ambos delegates, el compilador permite que utilicemos el mismo código sin necesidad de hacer nada extraño.
Hay otro feature de las lambda expressions que las hace particularmente interesantes y novedosas con respecto a los delegates. Existe un subconjunto de lambada expressions que pueden ser transformadas automáticamente en árboles de expresión, que pueden ser manipulados por el programador.
Extension Methods
Imaginemos que tenemos una clase que contiene datos de una Persona.
class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}
Si dada una persona queremos obtener el nombre completo utilizando un método, hay varias opciones. Agregar un método a la clase es lo más simple:
public string GetFullName()
{
return Name + " " + Surname;
}
También podemos agregar un método en una clase de utilidades que reciba un objeto de tipo Person y realice el trabajo:
namespace Helpers
{
static class PersonHelper
{
public static string GetFullName(Person person)
{
return person.Name + " " + person.Surname;
}
}
}
Ambas opciones son válidas pero se utilizan en forma diferente:
using Helpers;
class Program
{
public static void Main()
{
var person = new Person { Name = "Juan", Surname = "Pérez" };
Console.WriteLine(person.GetFullName()); // En el caso del método miembro.
Console.WriteLine(PersonHelper.GetFullName(person)); // En el caso del método estático.
}
}
La ventaja de escribir el método miembro es que permite utilizarlo en forma sencilla usando el operador punto, pero sólo es una alternativa viable cuando tenemos acceso al código fuente de nuestros objetos, ya que lo que estamos haciendo es cambiando la definición de nuestra clase. El hecho de definir un método, además, trae aparejada la desventaja de que el código que estamos agregando está fuertemente acoplado con la definición del tipo, ya que podemos ver los campos privados y protegidos del mismo, lo cual no siempre es deseable.
Por otra parte, la definición del método estático, tiene acceso únicamente a las propiedades y métodos expuestos públicamente, lo que fuerza un nivel menor de acoplamiento, además puede definirse para tipos que están fuera de nuestro control, cómo pueden ser los tipos del Framework. La principal desventaja de este método, es que es mucho menos conciso (más verbose), y en caso de querer utilizar más de un método en forma encadenada tendremos cosas así:
StringHelpers.Capitalize(PersonHelper.GetFullName(person)); // Feo de leer.
Con el advenimiento de la tercer versión de C#, se incorporaron al standard los llamados Extension Methods, los cuales permiten usar una sintaxis más amigable para el uso de los métodos estáticos que hemos discutido.
Básicamente, si marcamos el primer parámetro de un método estático con el modificador this, dicho método podrá ser invocado de la misma manera que lo son los miembros de un tipo.
Redefinamos la clase PersonHelper:
namespace Helpers
{
public static class PersonHelper
{
public static string GetFullName(this Person person)
{
return person.Name + " " + person.Surname;
}
}
}
Como podemos ver, la única diferencia con la definición anterior es el hecho de que incorporamos el modificador this. El haber hecho esto, nos permite escribir el siguiente código:
view source
print?
using Helpers;
class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}
class Program
{
public static void Main()
{
var p = new Person { Name = "José", Surname = "Gómez" };
Console.WriteLine(p.GetFullName()); // Se invoca cómo un método de instancia.
Console.WriteLine(PersonHelper.GetFullName(p)); // Se puede seguir invocando como método estático.
}
}
El código generado por el compilador es idéntico al de una invocación de un método estático normal, la única diferencia es el agregado (automático) del atributo ExtensionAttribute en la definición del método.
Otro ejemplo:
var fn1 = StringHelpers.Capitalize(PersonHelper.GetFullName(person));
var fn2 = person.GetFullName().Capitalize();
Console.WriteLine(fn1 == fn2); // Retorna true.
Para poder invocar un extension method, primero es necesario que su definición entre en el scope de resolución del archivo actual, para lo que se utiliza el keyword using junto con el namespace en el que está definido el extension method. Aún así, de existir un miembro con la misma firma en el tipo “extendido” dicho método tiene preferencia.
Si bien en esencia es un cambio menor, los extension methods mejoran mucho la legibilidad del código y habilitan la existencia de algo de gran importancia, LINQ, que será el tema de mi próximo post.
class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}
Si dada una persona queremos obtener el nombre completo utilizando un método, hay varias opciones. Agregar un método a la clase es lo más simple:
public string GetFullName()
{
return Name + " " + Surname;
}
También podemos agregar un método en una clase de utilidades que reciba un objeto de tipo Person y realice el trabajo:
namespace Helpers
{
static class PersonHelper
{
public static string GetFullName(Person person)
{
return person.Name + " " + person.Surname;
}
}
}
Ambas opciones son válidas pero se utilizan en forma diferente:
using Helpers;
class Program
{
public static void Main()
{
var person = new Person { Name = "Juan", Surname = "Pérez" };
Console.WriteLine(person.GetFullName()); // En el caso del método miembro.
Console.WriteLine(PersonHelper.GetFullName(person)); // En el caso del método estático.
}
}
La ventaja de escribir el método miembro es que permite utilizarlo en forma sencilla usando el operador punto, pero sólo es una alternativa viable cuando tenemos acceso al código fuente de nuestros objetos, ya que lo que estamos haciendo es cambiando la definición de nuestra clase. El hecho de definir un método, además, trae aparejada la desventaja de que el código que estamos agregando está fuertemente acoplado con la definición del tipo, ya que podemos ver los campos privados y protegidos del mismo, lo cual no siempre es deseable.
Por otra parte, la definición del método estático, tiene acceso únicamente a las propiedades y métodos expuestos públicamente, lo que fuerza un nivel menor de acoplamiento, además puede definirse para tipos que están fuera de nuestro control, cómo pueden ser los tipos del Framework. La principal desventaja de este método, es que es mucho menos conciso (más verbose), y en caso de querer utilizar más de un método en forma encadenada tendremos cosas así:
StringHelpers.Capitalize(PersonHelper.GetFullName(person)); // Feo de leer.
Con el advenimiento de la tercer versión de C#, se incorporaron al standard los llamados Extension Methods, los cuales permiten usar una sintaxis más amigable para el uso de los métodos estáticos que hemos discutido.
Básicamente, si marcamos el primer parámetro de un método estático con el modificador this, dicho método podrá ser invocado de la misma manera que lo son los miembros de un tipo.
Redefinamos la clase PersonHelper:
namespace Helpers
{
public static class PersonHelper
{
public static string GetFullName(this Person person)
{
return person.Name + " " + person.Surname;
}
}
}
Como podemos ver, la única diferencia con la definición anterior es el hecho de que incorporamos el modificador this. El haber hecho esto, nos permite escribir el siguiente código:
view source
print?
using Helpers;
class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}
class Program
{
public static void Main()
{
var p = new Person { Name = "José", Surname = "Gómez" };
Console.WriteLine(p.GetFullName()); // Se invoca cómo un método de instancia.
Console.WriteLine(PersonHelper.GetFullName(p)); // Se puede seguir invocando como método estático.
}
}
El código generado por el compilador es idéntico al de una invocación de un método estático normal, la única diferencia es el agregado (automático) del atributo ExtensionAttribute en la definición del método.
Otro ejemplo:
var fn1 = StringHelpers.Capitalize(PersonHelper.GetFullName(person));
var fn2 = person.GetFullName().Capitalize();
Console.WriteLine(fn1 == fn2); // Retorna true.
Para poder invocar un extension method, primero es necesario que su definición entre en el scope de resolución del archivo actual, para lo que se utiliza el keyword using junto con el namespace en el que está definido el extension method. Aún así, de existir un miembro con la misma firma en el tipo “extendido” dicho método tiene preferencia.
Si bien en esencia es un cambio menor, los extension methods mejoran mucho la legibilidad del código y habilitan la existencia de algo de gran importancia, LINQ, que será el tema de mi próximo post.
Partial classes en C#
Si estamos escribiendo clases utilizando un generador de código, muchas veces se nos presenta la necesidad de de modificar algunos aspectos de las clases generadas. Si bien es posible modificar el código generado, dicha práctica no es recomendable, ya que cualquier cambio que realicemos sería reemplazado en el momento en que regeneremos el código.
Es ahí donde entra el uso del keyword partial. Cuando definimos una clase como partial (e.g. partial class Test), estamos indicando al compilador de C#, que la definición de la misma se encuentra en múltiples archivos.
Esto nos permite generar el código de nuestra clase en un archivo (e.g. Test.g.cs) y permitir al usuario agregar métodos, propiedades o campos en otro archivo (e.g. Test.cs). De esta manera, cuando el generador cambie, o cuando cambiemos algún parámetro de generación, la parte que fue personalizada por el usuario de nuestro generador permanece incambiada.
Veamos un ejemplo sencillo. Generaremos clases con una propiedad con el mismo nombre de la clase y el sufijo ‘Member’ usando T4 (Text Template Transformation Toolkit) disponible en Visual Studio 2008
[Test.g.tt:]
<#@ template language="C#" #>
<#
string[] names = { "Class1", "Class2" };
foreach(string name in names) {
#>
public partial class <#=name#>
{
public int <#=name#>Member { get;set; }
}
<#
}
#>
Si todo fue bien, debajo de Test.g.tt deberíamos tener un archivo llamado Test.g.cs.
[Test.g.cs:]
public partial class Class1
{
public int Class1Member { get;set; }
}
public partial class Class2
{
public int Class2Member { get;set; }
}
Ahora supongamos que queremos que la clase Class1 tenga un miembro llamado NewMember. Podemos crear un archivo llamado Class1.cs.
[Class1.cs:]
partial class Class1
{
public string NewMember { get;set; }
}
Con esto, Class1 tiene ahora dos miembros y Class2 tiene uno solo. Si cambiamos Test.g.tt, el archivo Test.g.cs será reemplazado, pero nuestro código existente en Class1.cs no se verá afectado.
Es ahí donde entra el uso del keyword partial. Cuando definimos una clase como partial (e.g. partial class Test), estamos indicando al compilador de C#, que la definición de la misma se encuentra en múltiples archivos.
Esto nos permite generar el código de nuestra clase en un archivo (e.g. Test.g.cs) y permitir al usuario agregar métodos, propiedades o campos en otro archivo (e.g. Test.cs). De esta manera, cuando el generador cambie, o cuando cambiemos algún parámetro de generación, la parte que fue personalizada por el usuario de nuestro generador permanece incambiada.
Veamos un ejemplo sencillo. Generaremos clases con una propiedad con el mismo nombre de la clase y el sufijo ‘Member’ usando T4 (Text Template Transformation Toolkit) disponible en Visual Studio 2008
[Test.g.tt:]
<#@ template language="C#" #>
<#
string[] names = { "Class1", "Class2" };
foreach(string name in names) {
#>
public partial class <#=name#>
{
public int <#=name#>Member { get;set; }
}
<#
}
#>
Si todo fue bien, debajo de Test.g.tt deberíamos tener un archivo llamado Test.g.cs.
[Test.g.cs:]
public partial class Class1
{
public int Class1Member { get;set; }
}
public partial class Class2
{
public int Class2Member { get;set; }
}
Ahora supongamos que queremos que la clase Class1 tenga un miembro llamado NewMember. Podemos crear un archivo llamado Class1.cs.
[Class1.cs:]
partial class Class1
{
public string NewMember { get;set; }
}
Con esto, Class1 tiene ahora dos miembros y Class2 tiene uno solo. Si cambiamos Test.g.tt, el archivo Test.g.cs será reemplazado, pero nuestro código existente en Class1.cs no se verá afectado.
Partial Methods en C#
En el post anterior, vimos como habilitar la modificación de la estructura de una clase generada (o interfaz, o estructura) utilizando el keyword partial. De esta manera, podemos agregar nuevos métodos, campos, propiedades y eventos a una clase definida en otros archivos. Siguiendo con el tema, voy a presentar otro uso, que nos permite definir métodos opcionales.
Los partial methods funcionan en forma similar a los events, o a los delegates, pero sin tener costo en tiempo de ejecución en caso de no recibir una implementación, ya que el compilador elimina todas las referencias existentes al método en caso de no recibir una implementación.
Un partial method consta de dos partes: la definición, donde se indica el nombre y los parámetros que recibe el método (siempre retorna void); y la implementación, donde se repiten los mismos datos, pero se incluye un cuerpo que implementa el método.
Veamos un ejemplo. Supongamos que tenemos un generador que produce el siguiente código:
partial class Person
{
string name;
string surname;
public string Name
{
get { return name; }
set { name = value; }
}
public string Surname
{
get { return surname; }
set { surname = value; }
}
}
Con esta clase así generada, es difícil permitir a nuestros usuarios saber cuando cambió el nombre o el apellido de nuestra persona. Una posibilidad que tenemos es la de cambiar el generador para que agregue un evento por cada propiedad:
partial class Person
{
string name;
string surname;
public event Action<string> NameChanged = delegate {};
public event Action<string> SurnameChanged = delegate {};
public string Name
{
get { return name; }
set { name = value; NameChanged(name);}
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Esta versión es correcta, pero en el caso en que no requiramos monitorear todas las propiedades, hemos agregado eventos en forma innecesaria. Además, es una solución demasiado flexible, ya que puede haber múltiples suscriptores al evento, lo cual puede ser un problema.
La alternativa con partial methods sería esta:
partial class Person
{
string name;
string surname;
partial void NameChanged(string name);
partial void SurnameChanged(string surname);
public string Name
{
get { return name; }
set { name = value; NameChanged(name); }
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Si queremos saber cuando cambió la propiedad Name, podemos definir en otro archivo:
partial class Person
{
partial void NameChanged(string name)
{
Console.WriteLine("Name changed: '{0}'", name);
}
}
En este caso, la implementación de Person, tiene dos propiedades, Name y Surname, y únicamente un método, NameChanged. Cómo no definimos una implementación para SurnameChanged, el compilador omite todas las llamadas al método y lo elimina de la clase.
La generación de código es algo muy útil, que nos permite automatizar varias tareas mundanas, lo que mejora nuestra productividad. El uso de partial classes y partial methods nos permite mejorar las posibilidades de reúso de nuestros generadores.
Los partial methods funcionan en forma similar a los events, o a los delegates, pero sin tener costo en tiempo de ejecución en caso de no recibir una implementación, ya que el compilador elimina todas las referencias existentes al método en caso de no recibir una implementación.
Un partial method consta de dos partes: la definición, donde se indica el nombre y los parámetros que recibe el método (siempre retorna void); y la implementación, donde se repiten los mismos datos, pero se incluye un cuerpo que implementa el método.
Veamos un ejemplo. Supongamos que tenemos un generador que produce el siguiente código:
partial class Person
{
string name;
string surname;
public string Name
{
get { return name; }
set { name = value; }
}
public string Surname
{
get { return surname; }
set { surname = value; }
}
}
Con esta clase así generada, es difícil permitir a nuestros usuarios saber cuando cambió el nombre o el apellido de nuestra persona. Una posibilidad que tenemos es la de cambiar el generador para que agregue un evento por cada propiedad:
partial class Person
{
string name;
string surname;
public event Action<string> NameChanged = delegate {};
public event Action<string> SurnameChanged = delegate {};
public string Name
{
get { return name; }
set { name = value; NameChanged(name);}
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Esta versión es correcta, pero en el caso en que no requiramos monitorear todas las propiedades, hemos agregado eventos en forma innecesaria. Además, es una solución demasiado flexible, ya que puede haber múltiples suscriptores al evento, lo cual puede ser un problema.
La alternativa con partial methods sería esta:
partial class Person
{
string name;
string surname;
partial void NameChanged(string name);
partial void SurnameChanged(string surname);
public string Name
{
get { return name; }
set { name = value; NameChanged(name); }
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Si queremos saber cuando cambió la propiedad Name, podemos definir en otro archivo:
partial class Person
{
partial void NameChanged(string name)
{
Console.WriteLine("Name changed: '{0}'", name);
}
}
En este caso, la implementación de Person, tiene dos propiedades, Name y Surname, y únicamente un método, NameChanged. Cómo no definimos una implementación para SurnameChanged, el compilador omite todas las llamadas al método y lo elimina de la clase.
La generación de código es algo muy útil, que nos permite automatizar varias tareas mundanas, lo que mejora nuestra productividad. El uso de partial classes y partial methods nos permite mejorar las posibilidades de reúso de nuestros generadores.
Difference between Abstract Class & Interfase
Las clases abstractas pueden tener o no implementaciones de los métodos miembros de la clase, y las interfaces no tienen ninguna implementación solo las declaraciones de los miembros que deben constituir la clase que implemente dicha interfaz.
Los miembros de la interfaz deben ser públicos y sin implementación.
interface iMiInterfaz
{
int Sumar(int Num1, int Num2);
int Multiplicar(int Num1, int Num2);
}
Las clases abstractas pueden tener miembros abstractos y no abstractos (es decir sobrecargables en las clases que hereden de ella). pero en las interfaces todos los miembros son implicitamente abstractos, y todos los miembros de la interfaz deben ser reimplementados en la clase que derive de esta.
abstract class MiClaseAbstracta
{
public int Suma(int Num1, int Num2) { return Num1 + Num2; }
public abstract int Multiplicacion(int Num1, int Num2);
}
Los miembros de la interfaz deben ser públicos y sin implementación.
interface iMiInterfaz
{
int Sumar(int Num1, int Num2);
int Multiplicar(int Num1, int Num2);
}
Las clases abstractas pueden tener miembros abstractos y no abstractos (es decir sobrecargables en las clases que hereden de ella). pero en las interfaces todos los miembros son implicitamente abstractos, y todos los miembros de la interfaz deben ser reimplementados en la clase que derive de esta.
abstract class MiClaseAbstracta
{
public int Suma(int Num1, int Num2) { return Num1 + Num2; }
public abstract int Multiplicacion(int Num1, int Num2);
}
jueves, 12 de mayo de 2011
miércoles, 11 de mayo de 2011
lunes, 9 de mayo de 2011
Find duplicate values in SQL
SELECT user, COUNT(user) AS cant
FROM adm_users
GROUP BY user
HAVING COUNT(user)>1
FROM adm_users
GROUP BY user
HAVING COUNT(user)>1
martes, 3 de mayo de 2011
domingo, 1 de mayo de 2011
Suscribirse a:
Entradas (Atom)