El segundo grupo principal de tipos se llama tipos de referencia. Como recordatorio rápido, una variable de un tipo de referencia no contiene directamente datos, porque sólo almacena una referencia a los datos. En este grupo, puedes encontrar tres tipos incorporados, a saber, cadena, objeto y dinámico. Además, puedes declarar clases, interfaces y delegados.
A menudo existe la necesidad de almacenar algunos valores de texto. Puede conseguir este objetivo utilizando el tipo de referencia incorporado String del espacio de nombres System, que también está disponible mediante la palabra clave la palabra clave string. El tipo string es una secuencia de caracteres Unicode. Puede tener cero puede tener cero caracteres, uno o más caracteres, o la variable string puede ser nula.
Puede realizar varias operaciones con objetos de cadena, como la concatenación o el acceso a un particular usando el operador [], como se muestra a continuación:
string firstName = "Pedro", lastName = "Marquez";
int year = 1988;
string note = firstName + " " + lastName.ToUpper()
+ " nació en " + year;
string initials = firstName[0] + "." + lastName[0] + ".";
Al principio, se declara la variable firstName y se le asigna el valor “Pedro” a la misma. Del mismo modo, se establece “Perez” como valor de la variable lastName. En la tercera línea, se concatena cinco cadenas (utilizando el operador +), a saber, el valor actual de firstName, el espacio, el valor actual de lastName convertido a la cadena en mayúsculas (llamando al método ToUpper), la cadena " nació en ", y el valor actual de la variable y el valor actual de la variable año. En la última línea, se obtienen los primeros caracteres de las variables firstName y lastName obtenidos, utilizando el operador [], así como concatenados con dos puntos para formar las iniciales, es decir, P.M., que se almacenan como valor de la variable initials.
El método estático Format también puede utilizarse para construir la cadena, como se indica a continuación:
string note = string.Format("{0} {1} nació en {2}",
firstName, lastName.ToUpper(), year);
En este ejemplo, se especifica la cadena de formato compuesto con tres elementos de formato, a saber el nombre (representado por {0}), el apellido en mayúsculas ({1}) y el año ({2}). Los objetos a formatear se especifican como los siguientes parámetros.
También cabe mencionar la cadena interpolada, que utiliza expresiones interpoladas para construir una cadena. Para crear una cadena con este método, el carácter $ debe colocarse antes de ", como se muestra en el siguiente ejemplo:
string note = $"{firstName}{lastName.ToUpper()}
was born in {year}";
La clase Object, declarada en el espacio de nombres System, desempeña un papel muy importante en el desarrollo de aplicaciones en el lenguaje C# porque es la clase base para todas las clases. Eso significa que los tipos de valor y los tipos de referencia incorporados, así como los tipos definidos por el usuario se derivan de la clase Object, que también está disponible utilizando el alias object.
Como el tipo de objeto es la entidad base para todos los tipos de valor, significa que es posible convertir una variable de cualquier tipo de valor (por ejemplo, int o float) al tipo objeto, así como devolver una variable del tipo objeto a un tipo de valor específico. Estas operaciones se denominan boxing (la primera) y unboxing (la otra). Se muestran de la siguiente manera:
int age = 28;
object ageBoxing = age;int ageUnboxing = (int)ageBoxing;
Además de los tipos ya descritos, el dinámico está disponible para los desarrolladores. Este permite eludir la comprobación de tipos durante la compilación para poder realizarla durante el tiempo de ejecución. Este mecanismo es útil cuando se accede a algunas interfaces de programación de aplicaciones (API).
C# es un lenguaje orientado a objetos y admite la declaración de clases junto con varios miembros, incluyendo constructores, finalizadores, constantes, campos propiedades, indexadores, eventos, métodos y operadores, así como delegados. Además, las clases admiten la herencia y la implementación de interfaces. También hay miembros estáticos, abstractos y virtuales.
A continuación, se muestra una clase de ejemplo:
publicclassPerson
{
privatestring _location = string.Empty;
publicstring Name { get; set; }
publicint Age { get; set; }
publicPerson() => Name = "---";
publicPerson(string name, int age)
{
Name = name;
Age = age;
}
publicvoidRelocate(string location)
{
if (!string.IsNullOrEmpty(location))
{
_location = location;
}
}
publicfloatGetDistance(string location)
{
return DistanceHelpers.GetDistance(_location, location);
}
}
La clase Person contiene el campo privado _location con el valor por defecto establecido como cadena vacía (string.Empty), dos propiedades públicas (Name y Age), un constructor por defecto que establece un valor de la propiedad Name a — utilizando la definición del cuerpo de la expresión, un constructor adicional que toma dos parámetros y establece los valores de las propiedades, el método Relocate que actualiza el valor del campo privado, así como el método GetDistance que llama al método estático GetDistance de la clase DistanceHelpers y devuelve la distancia entre dos ciudades en kilómetros.
Se puede crear una instancia de la clase utilizando el operador new. A continuación, se puede realizar varias operaciones en el objeto creado, como llamar a un método, como se muestra a continuación:
Person person = new Person("Maria", 20);
person.Relocate("Rzeszow");
float distance = person.GetDistance("Warsaw");
En el punto anterior se mencionó una clase que podía implementar una o más interfaces.
Esto significa que dicha clase debe implementar todos los métodos, propiedades, eventos e indexadores que se especifican en todas las interfaces implementadas. Puede definir fácilmente interfaces en el lenguaje C# utilizando la palabra clave interface.
Como ejemplo, veamos el siguiente código:
publicinterfaceIDevice
{
string Model { get; set; }
string Number { get; set; }
int Year { get; set; }
voidConfigure(DeviceConfiguration configuration);
boolStart();
boolStop();
}
La interfaz IDevice contiene tres propiedades, a saber, las que representan un modelo de dispositivo (Model), el número de serie (Number) y el año de producción (Year). Además, tiene firmas de tres métodos, que son Configure, Start y Stop. Cuando una clase implementa la interfaz IDevice, debe contener las propiedades y métodos mencionados.
El tipo de referencia delegado permite especificar la firma requerida de un método.
El delegado podría entonces ser instanciado, así como invocado, como se muestra en el siguiente código:
delegatedoubleMean(double a, double b, double c);
staticdoubleHarmonic(double a, double b, double c)
{
return3 / ((1 / a) + (1 / b) + (1 / c));
}
staticvoidMain(string[] args)
{
Mean arithmetic = (a, b, c) => (a + b + c) / 3;
Mean geometric = delegate (double a, double b, double c)
{
return Math.Pow(a * b * c, 1 / 3.0);
};
Mean harmonic = Harmonic;
double arithmeticResult = arithmetic.Invoke(5, 6.5, 7);
double geometricResult = geometric.Invoke(5, 6.5, 7);
double harmonicResult = harmonic.Invoke(5, 6.5, 7);
}
En el ejemplo, el delegado Mean especifica la firma requerida del método para calcular el valor medio de tres números de punto flotante. Se instanciará con la expresión lambda (aritmética), el método anónimo (geométrico) y el método con nombre expresión lambda (aritmética), método anónimo (geométrica) y método con nombre (armónico). Cada delegado se invoca llamando al método Invoke.