Wednesday, November 21, 2007

TEMA 5:


Efectos de los entornos sobre los lenguajes

El desarrollo de un lenguaje de programación no tiene lugar en un vacío. El equipo que apoya a un lenguaje tiene gran impacto sobr el diseño del mismo. El lenguaje, como medio para resolver un problema, es parte de la tecnología global que se emplea. El entorno externo que apoya la ejecución de un programa se conoce como su entorno operativo u objetivo. El entorno en el cual un programa se diseña, se codifica, se pone a prueba y se depura, o entorno anfitrion, puede ser diferente del entorno operativo en el cual se usa el programa en el último término.

Cuatro clases de entornos objetivo cubren casi todas las aplicaciones de programación: de procesamiento por lotes, interactivo, de sistema incrustado y de programación (como caso especial de entorno interactivo). Cada uno plantea distintos requerimientos sore los lenguajes adaptados a esos entornos.

Entornos de procesamiento por lotes

El primero y más simple entorno operativo se compone ólo de archivos esternos de datos. Un programa toma un cierto conjunto de archivos de datos como entrada, procesa datos y produce un conjunto de archivos de salida; por ejemplo, un programa de nómina procesa dos archivos de entrada que contienen registros maestros de nómina y tiempos de periodos de paga semanales, y prodece dos archivos de salida que contienen registros maestros actualizados y cheques de sueldo. Este entorno operativo se designa como de procesamiento por lotes porque los datos de entrada se reunen en “lotes” de archivos y son procesados en lotes por el programa. Los lenguajes como FORTRAN, C, Pascal se proyectaron inicialmente para entornos de procesamiento por lotes, aunque ahora se pueden usar en un entorno interactivo o en un de sistema incrustado.

Efectos sobre el diseño de lenguajes

La influencia del entorno se aprecia en cuatro áreas principales: caracteristicas de entrada/salida, caracteristicas de manejo de errores y excepciones, recursos de regulación del tiempo y estructura de programas.

En un lenguaje proyectado para procesamiento por lotes por lo común de los archivos constituyen en la base para casi toda le estruvtura de entrada/salida. Aunque un archivo se puede usar para entrada/salida interactivo a una terminal, en estos lenguajes no se encaran las necesidades especiales de E/S interactiva. Por ejemplo, los archivos se guardan ordinariamente como regisros de longitud fija, pero en una terminal el programa necesitaria leer cada carácter conforme se introduce en el teclado. La estructura entrada/salida tampoco se ocupa típicamente de la necesidad de acceso a dispositivos especiales de E/S que se encuentran en sistemas incrustados.

Efectos sobre el diseño de lenguajes.

La influencia del entorno se aprecia en cuatro áreas principales:

  • Características de entrada/salida
  • Características de manejo de errores y excepciones
  • Recursos de regilación del tiempo
  • Estructura de programas

En un lenguaje proyactado para procesamiento por lotes, por lo común los archivos sonstituyen la base para casi toda la estructura de entrada/salida. Aunque un archivo se puede usar para entrada/salida interactiva a una terminal, en estos lenguajes no se encaran las necesidades especiales de la E/S interactiva. Por ejemplo, los archivos se guardan ordinariamente como registros de longitud fija, pero en una terminal el problema necesitaría leer cada cra´ter conforme se introduce en el teclado. La estructura de entrada/salida tampoco se ocupa típicamente de la necesidad de acceso a dispositivos especiales de E/S que se encuentran en sistemas incrustados.

En un entorno de procesamiento por lotes, un error que termine la ejecución del programa es aceptable aunque costoso, debido a que menudo se repite toda la ejecución despues de corregir el error. Además, en este entorno, no es posible la ayuda externa por parte del usuario para menejar o corregir el error de inmediato. Así pues, las facilidades del lenguaje para manejo de errores y excepciones enfatizan al manejo de éstos en el programa para que éste se pueda recuperar de casi todos los errores y continúe con el procesamiento sin suspender su ejecución.

Una tercera característica que distingue a un entorno de procesamiento por lotes es la carencia de resticciones de regulación de tiempo en un programa. Ordinariamente, el lenguaje no proporciona recursos para monitorear o afectar directamente la velocidad de ejecución del programa.

La estructura de programa repesentativa desde este entorno consiste en un programa principal y un conjunto de subprogramas. Esta estructura típica de programa está presente en FORTAN, C y Pascal. Los subprogramas, en su totalidad, son procesados por el compilador y éste interactúa rara vez con el programador durante el proceso de compilación.

Entornos interactivos

En un entorno interactivo, el más común en la actualidad en computadoras personales y estaciones de trabajo, un programa interactúa durante su ejecución directamente con un usuario en una consola de visualización, enviando alternativamente salidas hacia ésta y recibiendo entradas desde el teclado o ratón. Son ejemplos de los sistemas de procesamiento de tectos, hoojas de cálculo, juegos de video, sistemas de gestión de bases de datos y sistemas de instrucción asistida por computadora. Todos estos ejemplos son herramientas con las cuales el lector puede estar familiarizado.

Efectos sobre el diseño de lenguajes

Las característica de la entrada/salida interactiva son lo sumficientemente diferentes de las operaciones ordinarias con archivos como para que casi todos los lenguajes proyectados para un entorno de procesamiento por lotes experimenten cierta dificultad para adaptarse a un entorno interactivo. En C, por ejemplo, incluye funciones para tener acceso a líneas de texto desde un archivo y otras funciones que alimentan directamente de cada carácter conforme lo digita el usuario en la terminal. La INTRODUCCIÓN DIRECTA DE TEXTO DESDE UNA TERMINAL DE Pascal, sin embargo, suele ser muy engorrosa. Por esta razon, C y su derivación C++ se ha vuelto mucho más popular como lenguaje para escribir programas interactivos.


Entornos

Como hemos visto, la operación set! (asignación) nos permiten modelar objetos que tiene estado local. Cuando una asignación está presente, una variable no es sólo considerada como un nombre asociado a un valor, sino que la variable debe estar asociado a un “lugar” donde los valores se guarden. En el modelo de evaluación de Scheme, esos “lugares” se denominan entornos. Los entornos permiten formalizar también el concepto de ámbito de variable.

Definimos un "entorno" como un conjunto de variables y valores asociados a ellas. El entorno principal es el que existe cuando arrancamos el intérprete de Scheme.

Cuando usamos las formas especiales set! estamos modificando el entorno.

En el nuevo modelo de computación que veremos más adelante el concepto de entorno es central.

Una expresión se evalua en un entorno.

Ejemplo 1 de entorno:

(define x 5)
(set! x (+ 1 x))
x -> 6

Dibujo de los entornos resultantes:

Imagen del entorno 1

En el entorno global la variable x queda ligada al valor 5.

Imagen del entorno 1

Al evaluar la instrucción (set! x (+ 1 x)), la variable x incrementa en uno su valor.

Imagen del entorno 1

Se devuelve el valor ligado a x, que es 6.

Ejemplo 2 de entorno:

¿Qué pasa si la modificación de la variable x la hacemos en una llamada a una función?

(define x 5)
(define (prueba)
(set! x (+ 1 x))
x)
(prueba) -> 6
x -> 6

Dibujo de los entornos resultantes:

Imagen del entorno 2

En el entorno global la variable x queda ligada al valor 5.

Imagen del entorno 2

En el entorno global, el identificador prueba queda ligado a un procedimiento sin argumentos y cuyo cuerpo es (set! x (+ 1 x)) x). Éste a su vez está sociado al entorno global.

Imagen del entorno 2

Al hacer la llamada al procedimiento (prueba), se extiende el entorno asociado al procedimiento. En este entorno local a la función es donde se evalúa la función. Se incrementa la variable x en 1 y se devuelve.

Ejemplo 3 de entorno:

¿Qué pasa si dentro de la llamada a "prueba" se define otra variable x?

(define x 5)
(define (prueba)
(define x 10)
(set! x (+ 1 x))
x)
(prueba) -> 11
x -> 5

Dibujo del entorno resultante:

Imagen del entorno 3

La invocación a un procedimiento (como prueba en el ejemplo anterior) genera un entorno en el que se evalúa el cuerpo del procedimiento. Este entorno es algo así como la memoria local asociada a la evaluación del procedimiento. Una regla muy importante que veremos más adelante: este entorno local debe extender el entorno en el que fue creado el procedimiento que se invoca.

Estado local

Vamos a jugar con el ejemplo más sencillo posible de programación no funcional: un contador. Y vamos a utilizar este ejemplo para continuar introduciendo los elementos que posteriormente utilizaremos en el modelo de entornos.

Se trata de definir una funcion count que implemente un contador y devuelva cada vez un valor incrementado en 1.

(count) -> 0
(count) -> 1
(count) -> 2
...

Ejemplo 1: contador global

Vamos a jugar con el ejemplo más sencillo posible de programación no funcional: un contador.

Se trata de definir una funcion count que implemente un contador y devuelva cada vez un valor incrementado en 1.

; (count) -> 0
; (count) -> 1
; (count) -> 2
; ...
(define counter 0)

(define (count)
(set! counter (1+ counter))
counter)

(count) -> 1
(count) -> 2

Es necesario mejorar esta versión, porque la utilización de un contador global nos puede llevar a errores. Por ejemplo, alguna otra función puede cambiar el valor del contador sin que nos demos cuenta.

Vamos a intentar implementar en Scheme el estado local algo asi como el static de C. Buscamos poder definir un contador que vayamos incrementando y que podamos consultar a traves de una funcion, pero que no esté disponible globalmente.

int count(){
static int iCount = 0;

iCount += 1;
return iCount;
}

count = count(); // devuelve 1
count = count(); // devuelve 2

Ejemplo 2: primer intento de estado local (que no funciona).

Utiliza let para definir una variable local, pero no tiene estado ya que cada llamada a count vuelve a inicializar su valor a 0.

(define (count)
(let ((counter 0))
(set! counter (+ counter 1))
counter))

Ejemplo 3: Estado local (sí que funciona).

Usamos un constructor del contador (make-counter) que va a devolver el contador propiamente dicho.

(define (make-counter)
(define x 0)
(lambda ()
(set! x (+ counter 1))
x))

(define count (make-counter))
(count) ;-> 1
(count) ;-> 2

Nuevo modelo computacional: evaluacion del entorno. Cuando llamamos a lambda() para crear la función, ésta se crea en el entorno definido por la llamada a make-counter, en el que se ha creado la variable x con el valor 0. A partir de este momento la función creada por el lambda queda asociada a este entorno y usa x como una variable local. El valor de x no es visible desde el entorno global.

Los entornos resultantes son los siguiente:

Imagen del entorno ejemplo 3

En el entorno global, el identificador make-counter queda ligado a un procedimiento sin argumentos cuyo cuerpo es:

   (define x 0)
(lambda ()
(set! x (+ counter 1))
x)

Imagen del entorno ejemplo 3

En el entorno global, el identificador count queda ligado al resultado de la llamada a make-counter, que ha obligado a evaluarlo:

  • En primer lugar, se ha extendido el entorno asociado a make-counter. En este entorno local E1 a la función, la variable x ha quedado ligada al valor cero.
  • En segundo lugar, la evaluación de lambda(x).... ha devuelto un procedimiento, que ha quedado ligado al identificador count en el entorno global. Este procedimiento queda asociado al entorno local E1 que es donde fue creado.

Imagen del entorno ejemplo 3

La llamada a count obliga a evaluar este procedimiento, el cual extiende su entorno asociado. En este entorno local E2 se evalúa el cuerpo de la función, modificando el valor de x incrementándolo en 1.

Ejemplo 4: Variable local como parámetro

Igual que el 3, pero usando como variable local un parámetro de la funcion make-counter que representa el valor inicial del contador.

(define (make-counter x)
(lambda ()
(set! x (+ 1 x))
x))

(define count (make-counter 0))
(count) ;-> 1
(count) ;-> 2

Los entornos resultantes se muestran en la siguiente figura:

Imagen del entorno 5

En el entorno global, el identificador make-counter queda ligado a un procedimiento con un argumento x cuyo cuerpo es:

    (lambda ()
(set! x (+ counter 1))



x)

Imagen del entorno 5

En el entorno global, el identificador count queda ligado al resultado de la llamada a (make-counter 0), que ha obligado a evaluarlo:

  • En primer lugar, se ha extendido el entorno asociado a make-counter. En este entorno local E1 a la función, la variable x ha quedado ligada al valor cero (valor pasado como parámetro).
  • En segundo lugar, la evaluación de lambda(x).... ha devuelto un procedimiento, que ha quedado ligado al identificador count en el entorno global. Este procedimiento queda asociado al entorno local E1 que es donde fue creado.

Imagen del entorno 5

La llamada a count provoca la evaluación de este procedimiento, el cual extiende su entorno asociado. En este entorno local E2 se evalúa el cuerpo de la función, modificando el valor de x incrementándolo en 1.



Control de secuencia de procedimientos.

Las estructuras de secuencias se pueden clasificar en 3 grupos:

· Estructuras que se usan en expresiones, como reglas de precedencia y paréntesis;

· Estructuras que se usan entre enunciados o grupos de enunciados, como enunciados condicionales e iterativos; y

· Estructuras que se usan entre subprogramas, como llamadas de subprogramas y corrutinas.

Las estructuras de control de secuencias pueden ser implícitas o explícitas. Las estructuras de control de secuencias implícitas (o por omisión) son las que el lenguaje define que están en operación, a menos que sea modificado por alguna estructura explícita. Dentro de estas expresiones existen una jerarquía de operación definidas por el lenguaje, que controla el orden de ejecución de las operaciones de la expresión cuando no existe paréntesis.

Las estructuras explícitas de control de secuencias son las que el programa puede optar para modificar el orden implícito definido por el lenguaje; ejemplo, usando paréntesis dentro de las expresiones y etiquetas de enunciados.

Formas de control de secuencias al nivel de enunciados.

A nivel de enunciados hay tres formas principales de control de secuencia:

1.- Composición: Los enunciados pueden disponer de una serie textual de modo que deben ejecutarse en orden y siempre la ejecución de la estructura mayor del programa que contenga la serie.

2.- Alternancia: Dos series de enunciados pueden formar alternativas de modo que se ejecute una u otra serie, pero no las dos siempre que se ejecute la estructura mayor del programa que contenga la serie.

3.- Iteración: Cuando hay una serie de enunciados pueden ejecutar en forma repetitiva, cero o más veces; cuando es cero significa que se puede omitir del todo la ejecución. Para esto se debe ejecutar la estructura mayor del programa que contenga la serie.

Control explícito de secuencia.

Las maquinas se componían de posiciones de memoria, los primeros lenguajes por ejemplo (FORTRAM, angol) las modelaban con tipos de datos simples traducibles directamente a objetos de maquina ( FLOAT DE C ) Y ( REAL DE FORTRAM ) a punto flotante de hardware.

Enunciado goto: En muchos lenguajes están presentes dos forma de enunciado goto (ir a), uno es el enunciado goto incondicional y el otro es el enunciado goto condicional.

Goto incondicional: Dentro de una seré de enunciados, un goto incondicional, como:

Goto PROXIMO

GOTO CONDICIONAL: Dentro de una serie de enunciados, un goto condicional, como:

If A = 0 then goto próximo

En tanto los lenguajes contengan estructuras de control anidables , como las intrucciones while e if, las cuales se analizan en breve, el goto es un artefacto que se puede pasar en alto sin dificultad. Desdichadamente, en ciertos lenguajes, como las primeras versiones de FORTRAM y APL, la transferencia explícita del control es necesaria, puesto que faltan las estructuras compuestas apropiadas.

Enunciado break: Ciertos lenguajes, como C, incluyen un enunciado break ( escapar) como una forma de control explícito estructurado. Por lo común, el break causa que el control pase más adelante en el programa hasta un punto explícito al final de una estructura de control dada. Este break en C hace que el control salga del enunciado while, for o switch que lo encierra directamente.

En resumen el enunciado break cumple la función de que cauda la salida de una iteración, mientras que el continúe ocasiona una nueva iteración.

Diseño de programación estructurada

Algunas de las ventajas aparentes de los goto son:

1.- Manejo directo por el hardware para una ejecución eficiente si las etiquetas son simplemente marcas sintácticas locales en los enunciados.

2.- Sencillos y fáciles de usar en programas pequeños.

3.- Familiares para los programadores capacitados en lenguaje ensamblador o lenguajes más antiguos.

4.- De uso completamente general como bloque de construcción para representar ( simular ) cualquiera de otras formas de control que se analizan mas adelante.

Es más importante que el programa se ejecute correctamente en ves de tener la máxima eficiencia. El diseño de lenguajes de programación debe reflejar estas necesidades.

El concepto de la estructura de control de un punto de entrada y uno de salida contribuye a un diseño más inteligible. Un programa de mas de unos pocos enunciados es difícil de entender a menos que los enunciados estén organizados jerárquicamente en grupos, donde cada grupo represente una unidad conceptual del cómputo subyacente.

No es necesario que el orden de los enunciados en el programa corresponda al orden de ejecución. Mediante el uso de enunciado goto, es fácil escribir en los programas en los cuales el control salta entre diferentes series de enunciados siguiendo patrones irregulares. En este caso, el orden en que los enunciados aparecen en el programa tienen poca relación con el orden de ejecución de los mismos.

Programación estructurada

Este término se usa para el diseño de programas que hace énfasis en:

1.- El diseño jerárquico de estructuras de programas usando solo las formas simples de control, de composición, alternancia e iteración.

2.- La representación directa del diseño jerárquico en el texto del programa, usando los enunciados de control estructurados.

3.- El texto de programa en el cual orden textual de los enunciados corresponden al orden de ejecución.

4.- El uso de grupos de enunciados de propósito único, aun cuando sea necesario copiar los enunciados.

Control de secuencia estructurado

Casi todos los lenguajes suministran un conjunto de enunciados de control para expresar las formas básicas de control: composición, alternancia e iteración. Un aspecto importante de los enunciados que se van a analizar es que cada uno es un enunciado de entrada por salida, lo que significa de que cada enunciado hay un solo punto de entrada al enunciado y un solo punto de salida al mismo.

Los lenguajes más antiguos, como COBOL y FORTRAM, contienen algunos enunciados de control de entrada por salida, pero todavía se apoyan fuertemente en enunciados goto y etiquetas de enunciados. Ha sido difícil adaptar ambos lenguajes a conceptos modernos de lenguaje.

Enunciados compuestos

Ejemplos:

Begin

….. - serie de enunciados ( uno o más )

end

En C se escriben simplemente como {…}

Enunciados condicionales

Ejemplos:

Enunciados if. La ejecución opcional de un enunciado se expresa como un if de una bifurcación.

Ejemplo.

If condición then enunciado endif

Enunciados de iteración

La iteración suministra el mecanismo básico para los cálculos repetidos en casi todos los programas. La estructura básica de un enunciado de iteración consiste en una cabeza y un cuerpo. La cabeza controla el número de veces que el cuerpo se va a ejecutar, en tanto que el cuerpo es ordinariamente de enunciado ( compuesto ) que proporciona la acción del enunciado.

Repetición simple

El tipo mas sencillo de cabeza de enunciado de iteración especifica que el cuerpo se debe ejecutar cierto número fijo de veces. El PERFORM de COBOL es representativo de esta construcción:

Perform cuerpio K time

Hace que se evalúe K y luego se ejecute el cuerpo del enunciado ese número de veces.

Repetición mientras se cumple la condición. Se puede construir una iteración algo mas compleja usando una cabeza de repetir mientras. Una forma típica es:

While prueba do cuerpo

En esta forma de enunciado de iteración, la expresión de prueba se evalúa después de cada vez que se ha ejecutado el cuerpo.

Repetición mientras se incrementa un controlador. La tercera forma alternativa de enunciado de iteración es el enunciado cuya cabeza especifica una variable que sirve como un contador o índice durante la iteración. En la cabeza se especifica un valor inicial, un valor final y un incremento, y el cuerpo se ejecuta repetidamente usando primero el valor inicial como valor de la variable índice, luego el valor inicial mas el incremento, después el valor inicial mas dos veces el incremento, y así sucesivamente, hasta que se alcanza el valor final. En FORTRAM-77 ésta es la única forma de enunciado de iteración disponible. El enunciado for en ANGOL ilustra la estructura típica.

For l := l step 2 until 30 do cuerpo

En su forma general:

For k := N-1 step 2 * (W-1) until M * N do cuerpo

Repetición indefinida

Cuando hay problemas en la salida de la iteración, se suele emplear una iteración sin prueba explícita de terminación en la cabeza.

Ejemplo en ADA :

loop

…..

exit when condición:

end loop:

En pascal:

While cierta do begin…. End;

El enunciado for en C permite todos estos conceptos en una sola construcción:

For ( opción; opción; opción ) { cuerpo }

Implementación en enunciados iterativos

Para implementar una iteración for, las expresiones de la cabeza de la iteración que definen el valor final y el incremento se deben evaluar a la entrada final de la iteración y guardar en áreas especiales de almacenamiento temporal.

Problemas en el control de secuencia estructurada.

El enunciado goto se usa como último recurso cuando los enunciados de control son inadecuados, hay áreas que hacen innecesario el enunciado goto las cuales son:

ITERACIONES DE SALIDAS MULTIPLES

EJEMPLO DE ESTA ES :

For 1 en 1..K iteración

Exit when vect(1) = 0

End loop;

OTRO EJEMPLO ES EL DE DO-WHILE-DO:

Loop

Read ( x )

If fin-de-archivo then goto & { afuera de la iteración }

Process ( x )

End loop;

A do-while-do se le conoce como ( HACER MIENTRAS HACER ), ya que while “intermedio” maneja la secuencia.

OTRO EJEMPLO ES:

Dowhiledo

Read ( x )

While ( no fin_de_archivo )

Process ( x )

Endowhiledo;

Desafortunadamente ningún lenguaje implementa esta estructura, aunque en C, if ( condición ) BREAK se acerca y exit when de ADA es similar.

Se puede realizar cualquier tipo de problema con la unica aplicación de 3 estructuras de control:

  1. condicon;
  2. interacción:
  3. secuencia;

1.-Estructuras condición: Es el punto en el algoritmo en el que se condiciona el estado del proceso y se tienen dos o una alternativa; cuando es Verdadera o Falsa.

Si(expresión)entonces

Accion 1

Si no si(expresión2)entonces

Accion 2;

Sino

Accion n

Fin si.




2.-Estructuras de iteración: Es un mecanismo de lazo. Permite repetir varias veces un grupo de pasos, hasta que se satisfaga esta condición. La repetición puede programarse para un cierto número de veces.

Accion 1

Accion 2

Accion 3

Accion 3

·

·

·

Accion n

·

·

Accion n

http://dis.um.es/%7Elopezquesada/documentos/FP0405/Proyecto/web4/webalgo/teoria_archivos/image011.gif


3.- Estructura de control de secuencia: Es un grupo de instrucciones que se ejecuta en orden, de la primera a la última. Una acción hasta que se vuelva Verdadera la condición.

Accion 1

Accion 2

Accion 3

Accion 4

Accion x

…..

Accion N



http://dis.um.es/%7Elopezquesada/documentos/FP0405/Proyecto/web4/webalgo/teoria_archivos/image012.gif


Ejemplo 1 :

Leer un número y decidir si es menor que 10

Ejemplo 2 :

Modificar el anterior para ver si el número es mayor o igual a 10 (dos formas).

Ejemplo 3 :

Modificar el anterior para ver si el número es mayor o igual a 5 y menor que 10.

Ejemplo 4 :

Modificar el anterior para ver si el número es: mayor o igual a 5 y menor que 10 o si es mayor que 25 y menor que 32.

Ejemplo 5 :

Leer un texto y decir si es igual a: Marc

Modificadores de Acceso.

Estos modificadores son partículas que se les colocan delante para
indicar desde qué códigos puede accederse a ellos, entendiéndose por
acceder el hecho de usar su nombre para cualquier cosa que no sea
definirlo, como llamarlo si es una función, leer o escribir su valor
si es un campo, crear objetos o heredar de él si es una clase, etc.

Por defecto se considera que los miembros de un tipo de dato sólo
son accesibles desde código situado dentro de la definición del mismo,
aunque esto puede cambiarse precediéndolos de uno los siguientes
modificadores (aunque algunos de ellos ya se han explicado a lo largo
del tema, aquí se recogen todos de manera detallada) al definirlos:

* public: Puede ser accedido desde cualquier código.
* protected: Desde una clase sólo puede accederse a miembros
protected de objetos de esa misma clase o de subclases suyas. Así, en
el siguiente código las instrucciones comentadas con // Error no son
válidas por lo escrito junto a ellas:


public class A
{
protected int x;
static void F(A a, B b, C c)
{
a.x = 1; // Ok
b.x = 1; // Ok
c.x = 1; // OK
}
}

public class B: A
{
static void F(A a, B b, C c)
{
//a.x = 1; // Error, ha de accederse a traves de objetos tipo B o C
b.x = 1; // Ok
c.x = 1; // Ok
}
}

public class C: B
{
static void F(A a, B b, C c)
{
//a.x = 1; // Error, ha de accederse a traves de objetos tipo C
//b.x = 1; // Error, ha de accederse a traves de objetos tipo C
c.x = 1; // Ok
}
}


Obviamente siempre que se herede de una clase se tendrá total
acceso en la clase hija –e implícitamente sin necesidad de usar la
sintaxis .- a los miembros que ésta herede de su
clase padre, como muestra el siguiente ejemplo:


using System;
class A
{
protected int x=5;
}

class B:A

{
B()
{
Console.WriteLine("Heredado x={0} de clase A", x);
}

public static void Main()
{
new B();
}
}


Como es de esperar, la salida por pantalla del programa de ejemplo será:


Heredado x=5 de clase A



A lo que no se podrá acceder desde una clase hija es a los miembros
protegidos de otros objetos de su clase padre, sino sólo a los
heredados. Es decir:


using System;
class A
{
protected int x=5;
}

class B:A
{
B(A objeto)
{
Console.WriteLine("Heredado x={0} de clase A", x);
Console.WriteLine(objeto.x); // Error, no es el x heredado
}

public static void Main()
{
new B(new A());
}
}

* private: Sólo puede ser accedido desde el código de la clase a
la que pertenece. Es lo considerado por defecto.
* internal: Sólo puede ser accedido desde código perteneciente al
ensamblado en que se ha definido.
* protected internal: Sólo puede ser accedido desde código
perteneciente al ensamblado en que se ha definido o desde
clases que deriven de la clase donde se ha definido.

Si se duda sobre el modificador de visibilidad a poner a un
miembro, es mejor ponerle inicialmente el que proporcione menos
permisos de accesos, ya que si luego detecta que necesita darle más
permisos siempre podrá cambiárselo por otro menos restringido. Sin
embargo, si se le da uno más permisivo de lo necesario y luego se
necesita cambiar por otro menos permisivo, los códigos que escrito en
base a la versión más permisiva que dependiesen de dicho miembro
podrían dejar de funcionar por quedarse sin acceso a él.

Es importante recordar que toda redefinición de un método virtual
o abstracto ha de realizarse manteniendo los mismos modificadores que
tuviese el método original. Es decir, no podemos redefinir un método
protegido cambiando su accesibilidad por pública, pues si el creador
de la clase base lo definió así por algo sería.

Respecto a los tipos de datos, por defecto se considera que son
accesibles sólo desde el mismo ensamblado en que ha sido definidos,
aunque también es posible modificar esta consideración anteponiendo
uno de los siguientes modificadores a su definición:

* public: Es posible acceder a la clase desde cualquier ensamblado.
* internal: Sólo es posible acceder a la clase desde el ensamblado
donde se declaró. Es lo considerado por defecto.

También pueden definirse tipos dentro de otros (tipos internos) En
ese caso serán considerados miembros del tipo contenedor dentro de la
que se hayan definido, por lo que les serán aplicables todos los
modificadores válidos para miembros y por defecto se considerará que,
como con cualquier miembro, son privados. Para acceder a estos tipos
desde código externo a su tipo contenedor (ya sea para heredar de
ellos, crear objetos suyos o acceder a sus miembros estáticos), además
de necesitarse los permisos de acceso necesarios según el modificador
de accesibilidad al definirlos, hay que usar la notación
., como muestra en este
ejemplo:


// No lleva modificador, luego se considera que es internal
class A
{
// Si ahora no se pusiese public se consideraría private
public class AInterna {}
}

// B deriva de la clase interna AInterna definida dentro de A.
// Es válido porque A.AInterna es pública
class B:A.AInterna
{}

Nótese que dado que los tipos externos están definidos dentro de
su tipo externo, desde ellos es posible acceder a los miembros
estáticos privados de éste. Sin embargo, hay que señalar que no pueden
acceder a los miembros no estáticos de su tipo contenedor.



Páginas utilizadas:

http://www.lsi.upc.es/~euetit/tp/recur.pdf

http://html.rincondelvago.com/secuencias.html

http://computacion.cs.cinvestav.mx/~acaceres/courses/itesm/lp/clases/lp07.pdf