Los operadores sirven, para efectuar operaciones con uno o más parámetros
(sumar, restar, comparar...) y retornar un resultado. Aquí se muestra una tabla
con los operadores de C#.
Tomado de el Guille |
Están colocados en orden de precedencia, es decir, en una expresión con
varios de ellos, se ejecutan en ese orden. Los operadores primarios son
operadores de expresión.
En el caso de “(expresión)”, los operadores son realmente los paréntesis.
Sirven para modificar la precedencia.
En “objeto.miembro”, el operador es el punto. Sirve para especificar un
miembro de una clase (sea una variable, una propiedad o un método).
En “método(argumento, argumento, ...)”, los operadores vuelven a ser los
paréntesis. En este caso, sirven para especificar la lista de argumentos de un
método.
En array[índice], los operadores son los corchetes. Sirven para indicar el
elemento de un array o un indizador.
Los operadores de incremento (++) y decremento (--) sirven para incrementar
o disminuir el valor de una variable en una unidad. Por ejemplo:
num++;
Hará que num incremente su valor en una unidad. Los operadores de
incremento y decremento se pueden poner delante (preincremento ó predecremento)
o bien detrás (postincremento ó postdecremento), teniendo comportamientos
distintos.
Si hay un postincremento o postdecremento, primero se toma el valor de la
variable y después se incrementa o decrementa. En caso contrario, si lo que hay
es un preincremento o un predecremento, primero se incrementa o decrementa la
variable y después se toma el valor de la misma. En una línea como la anterior
esto no se ve claro, porque, además, el resultado sería el mismo que poniendo
++num. Sin embargo, veamos este otro ejemplo (num vale 10):
a = ++num;
b = a--;
Después de ejecutar la primera línea, tanto a como num valdrían 11, ya que
el preincremento hace que primero se incremente num y después se tome su valor,
asignándolo así a la variable a. Después de ejecutar la segunda línea, b valdrá
11, y a valdrá 10 porque el postdecremento de a hace que primero se asigne su
valor actual a b y después se decremente el suyo propio.
El operador “new” sirve para instanciar objetos.
El operador “typeof” es un operador de reflexión. La reflexión es la
posibilidad de recuperar información de un tipo determinado en tiempo de
ejecución. typeof devuelve el tipo de un objeto.
“sizeof” devuelve el tamaño en bytes que ocupa un tipo determinado. Solamente
se puede utilizar sizeof con tipos valor y en contextos de código inseguro.
“checked” y “unchecked” sirven para controlar si una expresión provoca o no
desbordamiento. Por ejemplo, las variables de tipo byte pueden almacenar valores
entre 0 y 255. Si escribimos:
byte i=253;
checked {i+=10;}
Console.WriteLine(i);
El programa se compila, pero al ejecutar se produce un error de
desbordamiento, ya que la variable i es de tipo byte y no puede almacenar
valores mayores que 255. Sin embargo, si cambiamos checked por unchecked:
byte i=253;
unchecked {i+=10;}
Console.WriteLine(i);
El programa no produciría error de desbordamiento, ya que unchecked hace
que se omitan estos errores. En su lugar, i toma el valor truncado, de modo que
después de esa línea valdría 7. checked
y unchecked son bloques de código, como se deduce al ver que está
escrito con llaves, de modo que se pueden incluir varias líneas en un bloque
checked o unchecked.
Las variables numéricas tienen un rango de datos limitado, por ejemplo, una
variable de tipo byte, no puede almacenar valores menores que 0 ni mayores que
255. Cuando se trunca el valor, lo que hace es colocar seguidos en una lista
todos los valores que la variable acepta, empezando de nuevo por el primero
cuando el rango acaba, y después se va recorriendo esta lista hasta terminar la
operación de suma.
En la primera fila están la lista de valores que acepta la variable, a
continuación del último que acepta vuelve a estar el primero. Al sumarle 10
(segunda fila) es como si se fueran contando valores posibles hacia adelante,
de modo que i ahora vale 7. Otro ejemplo usando el tipo sbyte (que acepta
valores entre -128 y 127):
sbyte i=126;
unchecked {i+=10;}
Console.WriteLine(i);
Los operadores unitarios + y - sirven para mantener o cambiar el signo de
un operando. Si se desea mantener el signo de un operando sin cambios, el + se
puede omitir. Por ejemplo:
int i=10;
int b=-i;
Console.WriteLine("Valor de i: {0} Valor de b: {1}", i, b);
La variable i valdría 10, y la variable b valdría -10. Por lo tanto, la
salida en la consola sería:
Valor de i: 10 Valor de b: -10
El operador unitario ! es un not lógico, invierte el valor de un dato de
tipo boolean. En el siguiente ejemplo, i valdría true y b valdría false:
bool i=true;
bool b=!i;
El operador unitario ~ es de complemento a nivel de bits, o sea, que
devuelve el valor complementario al operando al que afecta. Para entender esto
usamos una variable de tipo int y escribimos su valor y el de su complementario
en hexadecimal:
uint i=10;
Console.WriteLine("Valor de i: {0:X8} Valor de ~i: {1:X8}",
i, ~i);
La salida en la consola sería la siguiente:
Valor de i: 0000000A Valor de ~i: FFFFFFF5
El operador ~ ha cambiado todos los bits, poniendo un 1 donde había un 0 y
un 0 donde había un uno. El operador ~ solamente es aplicable a variables de
tipo int, uint, long y ulong.
En el operador (conversion), lo que ha de ir entre paréntesis es el tipo al
que se quiere convertir (int), (uint), (long)... Ya se explicó en el primer
capítulo en el sistema de tipos.
Los operadores * y / son, respectivamente, para multiplicar y dividir.
El operador % devuelve el resto de una división. Por ejemplo, 8 % 3 devolvería
2.
Los operadores + y – (binarios) son para sumar o restar..
Los operadores << y >> efectúan un desplazamiento de bits hacia
la izquierda o hacia la derecha. Ejemplo, usando números hexadecimales:
int i=15;
int b;
int c;
Console.WriteLine("Valor de i: {0:X}", i);
b = i >> 1;
Console.WriteLine("Ejecutado b = i >> 1;");
Console.WriteLine("Valor de b: {0:X}", b);
c = i << 1;
Console.WriteLine("Ejecutado c = i << 1;");
Console.WriteLine("Valor de c: {0:X}", c);
La salida en la consola queda:
Valor de i: F
Ejecutado b = i >> 1;
Valor de b: 7
Ejecutado b = i << 1;
Vaor de b: 1E
Variable
|
Valor hex.
|
Valor binario
|
i
|
0000000F
|
0000 0000 0000 0000 0000 0000 0000 1111
|
b
|
00000007
|
0000 0000 0000 0000 0000 0000 0000 0111
|
c
|
0000001E
|
0000 0000 0000 0000 0000 0000 0001 1110
|
A la variable b se le asigna lo que vale i desplazando sus bits hacia la
derecha en una unidad. El 1 que había más a la derecha se pierde. En la
variable c asigna lo que valía i
desplazando sus bits hacia la izquierda también en una unidad. En la parte
derecha se rellena el hueco con un cero.
Los operadores relacionales < (menor que), > (mayor que), <=
(menor o igual que), >= (mayor o igual que), is, == (igual que), !=
(distinto de) establecen una comparación entre dos valores y devuelven como
resultado un valor de tipo booleano (true o false).
El resultado es muy obvio cuando se trata de números (tipos valor). Sin
embargo, cuando utilizamos variables de tipo referencia no es tan intuitivo.
Circunferencia c = new Circunferencia(4);
Circunferencia d = new Circunferencia(4);
Console.WriteLine("c==d devuelve: {0}", (c==d));
El resultado de comparar c==d sería False. A pesar de que ambos
objetos sean idénticos el resultado es False porque una variable de un tipo
referencia no retorna internamente un dato específico, sino un puntero a la
dirección de memoria donde está almacenado el objeto. De este modo, al
comparar, el sistema compara los punteros en lugar de los datos del objeto, y,
por lo tanto, devuelve False, puesto que las variables c y d no apuntan a la
misma dirección de memoria.
Para hacer esta comparación tenemos que utilizar el método Equals heredado
de la clase object así:
Circunferencia c = new Circunferencia(4);
Circunferencia d = new Circunferencia(4);
Console.WriteLine("c.Equals(d) devuelve:
{0}", c.Equals(d));
Ahora, el resultado sí sería True.
El operador is devuelve un valor boolean al comparar si un objeto (de un
tipo referencia) es compatible con una clase. Por ejemplo:
Circunferencia c=new Circunferencia();
Console.WriteLine("El resultado de c is Circunferencia es: {0}",
(c is Circunferencia));
La salida de estas dos líneas sería :
El resultado de c is Circunferencia es: True
Decir que el objeto es compatible con la clase significa que ese objeto se
puede convertir a esa clase. Si el objeto contiene una referencia nula (null) o
si no es compatible con la clase, el operador is retornará false.
Los operadores & (and a nivel de bits), | (or a nivel de bits) y ^ (xor
-o exclusivo- a nivel de bits) hacen una comparación binaria (bit a bit) de dos
números devolviendo el resultado de dicha comparación como otro número. Un
ejemplo:
int i=10;
int b=7;
int res;
res = i & b;
Console.WriteLine("{0} & {1} retorna: Decimal: {2} Hexadecimal:
{3:X}", i, b, res, res);
res = (i | b);
Console.WriteLine("{0} | {1} retorna: Decimal: {2} Hexadecimal:
{3:X}", i, b, res, res);
res = (i ^ b);
Console.WriteLine("{0} ^ {1} retorna: Decimal: {2} Hexadecimal: {3:X}",
i, b, res, res);
La salida en pantalla sería esta:
10 & 7 retorna: Decimal: 2 Hexadecimal: 2
10 | 7 retorna: Decimal: 15 Hexadecimal: F
10 ^ 7 retorna: Decimal: 13 Hexadecimal: D
En ninguno de los casos se ha hecho una suma normal de los dos números.
Veamos estas tres operaciones. Las operaciones se hacen bit a bit. Marcamos en
negrita los valores que provocan que el bit resultante en esa posición valga 1:
Operación: i & b
Variable
|
Valor dec.
|
Valor hex.
|
Valor binario
|
i
|
10
|
A
|
0000 0000 0000 0000 0000 0000 0000 1010
|
b
|
7
|
7
|
0000 0000 0000 0000 0000 0000 0000 0111
|
Resultado
|
2
|
2
|
0000 0000 0000 0000 0000 0000 0000 0010
|
Operación: i | b
Variable
|
Valor dec.
|
Valor hex.
|
Valor binario
|
i
|
10
|
A
|
0000 0000 0000 0000 0000 0000 0000 1010
|
b
|
7
|
7
|
0000 0000 0000 0000 0000 0000 0000 0111
|
Resultado
|
15
|
F
|
0000 0000 0000 0000 0000 0000 0000 1111
|
Operación: i ^ b
Variable
|
Valor dec.
|
Valor hex.
|
Valor binario
|
i
|
10
|
A
|
0000 0000 0000 0000 0000 0000 0000 1010
|
b
|
7
|
7
|
0000 0000 0000 0000 0000 0000 0000 0111
|
Resultado
|
13
|
D
|
0000 0000 0000 0000 0000 0000 0000 1101
|
Los operadores && (AND lógico) y || (OR lógico) comparan dos
valores de tipo booleano y retornan como resultado otro valor de tipo booleano.
El operador ? : (question) evalúa una expresión como true o false y
devuelve un valor que se le especifique en cada caso ( equivale más o menos a
la función iif de Visual Basic). Ejemplo:
string mensaje = (num == 10) ? "El número es 10": "El
número no es 10";
Delante del interrogante se pone la expresión que debe retornar un valor
booleano. Si dicho valor es true, el operador retornará lo que esté detrás del
interrogante, y si es false retornará lo que esté detrás de los dos puntos. No
tiene por qué retornar siempre un string. Puede retornar un valor de cualquier
tipo. Por lo tanto, si en este ejemplo num valiera 10, la cadena que se
asignaría a mensaje sería "El número es 10", y en caso contrario se
le asignaría "El número no es 10".
El operador de asignación (=) asigna lo que hay a la derecha del mismo en
la variable que está a la izquierda. Por ejemplo, la expresión a = b asignaría
a la variable a lo que valga la variable b. Ambas variables han de ser
compatibles. No se puede asignar un
número a una cadena, y viceversa, tampoco es posible asignar una cadena a una
variable de algún tipo numérico.
int a=5;
int b=a;
b++;
Tras la ejecución de estas tres líneas, a valdría 5 y b valdría 6. Si utilizamos variables de tipo referencia:
Circunferencia a=new Circunferencia();
a.Radio=4;
Circunferencia b=a;
b.Radio++;
Tanto a como b serán objetos de la clase circunferencia. Después de la
ejecución de estas líneas, el radio de la circunferencia b valdrá 5 y el de la
circunferencia a también 5. Lo
que ha ocurrido aquí, es que al hacer la asignación Circunferencia b=a; no se
ha reservado un nuevo espacio de memoria para la circunferencia b pues, al
tratarse de una variable de tipo referencia, devuelve internamente un puntero a
la dirección reservada para este objeto, es este puntero el que se ha asignado
a la variable b, de modo que las
variables a y b apuntan ambas al mismo espacio de memoria. Por lo tanto,
cualquier modificación que se haga en el objeto usando alguna de estas
variables quedará reflejado también en la otra variable, ya que, en realidad,
son la misma cosa. Si quisiéramos objetos distintos, es decir, espacios de
memoria distintos en la memoria, tendremos que instanciarlos por separado, y posteriormente
asignar los valores a las propiedades una por una:
Circunferencia a=new Circunferencia();
a.Radio=4;
Circunferencia b=new Circunferencia();
b.Radio=a.Radio;
b.Radio++;
en este caso, el radio de la circunferencia a será 4 y el de la
circunferencia b será 5. Hay que ser cuidadosos con esto, pues si hubiéramos escrito el código de la
siguiente forma:
Circunferencia a=new Circunferencia();
a.Radio=4;
Circunferencia b=new Circunferencia();
b=a;
b.Radio++;
Hubiera ocurrido algo parecido al primer caso. A pesar de haber instanciado
el objeto b por su lado (reservando así un espacio de memoria para b distinto
del de a), al asignar b=a, la referencia de b se destruye, asignándosele de
nuevo la de a, de modo que ambas variables volverían a apuntar al mismo objeto
dando el mismo resultado que en el primer caso, es decir, que el radio de ambas
circunferencias sería 5.
El resto de operadores de asignación son operadores compuestos a partir de
otro operador y el operador de asignación. Los operadores *=, /=, %=, +=,
-=, <<=, >>=, &=, ^=, |=
num *= 10; // Equivale a num = num * 10
num /= 10; // Equivale a num = num / 10
num %= 10; // Equivale a num = num % 10
num += 10; // Equivale a num = num + 10
num -= 10; // Equivale a num = num - 10
num <<= 10; // Equivale a num = num << 10
num >>= 10; // Equivale a num = num >> 10
num &= 10; // Equivale a num = num & 10
num ^= 10; // Equivale a num = num ^ 10
num |= 10; // Equivale a num = num | 10
La precedencia de operadores determina la prioridad con la que se ejecutan
cuando hay varios de ellos en una misma expresión. Pues el resultado puede ser
distinto en función del orden en que se ejecuten. Por ejemplo. La expresión 4 +
3 * 6 - 8 devolvería 14, ya que primero se hace la multiplicación y después las
sumas y las restas. Si queremos modificar dicha precedencia hay que utilizar
paréntesis: (4+3)*6-8 lo que devuelve 34, pues primero se ejecuta lo que hay
dentro del paréntesis, después la multiplicación y, por último, la resta.
No hay comentarios:
Publicar un comentario