Usos prácticos para la palabra clave “interna” en C #

¿Podría explicarnos qué usos prácticos tiene la palabra clave internal en C #?

Sé que el modificador internal limita el acceso al ensamblaje actual, pero ¿cuándo podría necesitarlo?

Clases / métodos de utilidad o ayudantes a los que le gustaría acceder desde muchas otras clases dentro del mismo ensamblado, pero a los que desea asegurarse de que el código en otros ensamblajes no pueda acceder.

Desde MSDN :

Un uso común del acceso interno está en el desarrollo basado en componentes porque permite que un grupo de componentes coopere de forma privada sin estar expuesto al rest del código de la aplicación. Por ejemplo, un marco para construir interfaces gráficas de usuario podría proporcionar clases de Control y Formularios que cooperen utilizando miembros con acceso interno. Como estos miembros son internos, no están expuestos al código que usa el marco.

También puede usar el modificador interno junto con el atributo de nivel de ensamblaje InternalsVisibleTo para crear ensamblajes “amigos” a los que se les concede acceso especial a las clases internas del ensamblaje de destino.

Esto puede ser útil para la creación de conjuntos de pruebas unitarias que luego pueden llamar a los miembros internos del conjunto que se probarán. Por supuesto, a ningún otro ensamblado se le concede este nivel de acceso, por lo que cuando libera su sistema, se mantiene la encapsulación.

Si Bob necesita BigImportantClass, Bob necesita que las personas que poseen el proyecto A se inscriban para garantizar que BigImportantClass se escribirá para satisfacer sus necesidades, se probará para garantizar que satisfaga sus necesidades, se documenta que satisface sus necesidades, y que un proceso se pondrá en marcha para garantizar que nunca se modifique para que ya no satisfaga sus necesidades.

Si una clase es interna, no tiene que pasar por ese proceso, lo que ahorra un presupuesto para el Proyecto A que pueden gastar en otras cosas.

El punto de interno no es que le haga la vida difícil a Bob. Es que le permite controlar las costosas promesas que hace el Proyecto A sobre características, duración, compatibilidad, etc.

Otra razón para usar interna es si ofuscas tus binarios. El ofuscador sabe que es seguro codificar el nombre de clase de cualquier clase interna, mientras que el nombre de las clases públicas no se puede codificar, porque eso podría romper las referencias existentes.

Si está escribiendo un archivo DLL que encapsula una tonelada de funcionalidad compleja en una API pública simple, entonces se usa “interno” en los miembros de la clase que no deben exponerse públicamente.

La ocultación de la complejidad (también conocida como encapsulación) es el concepto principal de ingeniería de software de calidad.

La palabra clave interna se utiliza mucho cuando está comstackndo un contenedor sobre código no administrado.

Cuando tienes una biblioteca basada en C / C ++ que quieres importar a Dll, puedes importar estas funciones como funciones estáticas de una clase, y hacer que sean internas, por lo que tu usuario solo tiene acceso a tu contenedor y no a la API original, por lo que no puede meterse con cualquier cosa. Como las funciones son estáticas, puede usarlas en todas partes del ensamblaje para las múltiples clases contenedoras que necesite.

Puedes echar un vistazo a Mono.Cairo, es un contenedor de la biblioteca de El Cairo que utiliza este enfoque.

Impulsado por la regla de “usar el modificador más estricto como sea posible”, uso interno en todas partes que necesito para acceder, por ejemplo, al método de otra clase hasta que explícitamente necesito acceder a él desde otro ensamblado.

Como la interfaz de ensamblado suele ser más estrecha que la sum de sus interfaces de clases, hay bastantes lugares donde la utilizo.

Me parece que el uso interno es demasiado usado. no deberías estar exponiendo ciertas funciones solo a ciertas clases que no harías a otros consumidores.

Esto en mi opinión rompe la interfaz, rompe la abstracción. Esto no quiere decir que nunca se deba usar, pero una mejor solución es refactorizar a una clase diferente o, si es posible, usarla de otra manera. Sin embargo, esto puede no ser siempre posible.

Las razones por las que puede causar problemas es que a otro desarrollador se le puede cargar la construcción de otra clase en el mismo ensamblaje que el suyo. Tener internos reduce la claridad de la abstracción y puede causar problemas si se usa incorrectamente. Sería el mismo problema que si lo hiciera público. La otra clase que está siendo desarrollada por el otro desarrollador sigue siendo un consumidor, al igual que cualquier clase externa. La abstracción y encapsulación de clases no es solo para la protección de / para clases externas, sino para cualquier clase.

Otro problema es que muchos desarrolladores pensarán que pueden necesitar usarlo en otro lugar del ensamblaje y marcarlo como interno de todos modos, aunque no lo necesiten en ese momento. Otro desarrollador puede pensar que está allí para tomar. Por lo general, desea marcar en privado hasta que tenga una necesidad definitiva.

Pero parte de esto puede ser subjetivo, y no estoy diciendo que nunca deba usarse. Solo use cuando sea necesario.

Vi uno interesante el otro día, tal vez la semana, en un blog que no recuerdo. Básicamente no me puedo atribuir el mérito de esto, pero pensé que podría tener alguna aplicación útil.

Supongamos que desea que una clase abstracta sea vista por otra asamblea, pero no desea que alguien pueda heredar de ella. Sealed no funcionará porque es abstracto por una razón, otras clases en ese ensamblaje heredan de él. Privado no funcionará porque es posible que desee declarar una clase para padres en algún lugar del otro ensamblaje.

 namespace Base.Assembly
 {
   clase abstracta pública Parent
   {
     vacío abstracto interno SomeMethod ();
   }

   // Esto funciona bien ya que está en el mismo ensamblaje.
   clase pública ChildWithin: Parent
   {
     anulación interna anulada SomeMethod ()
     {
     }
   }
 }

 namespace Another.Assembly
 {
   // Kaboom, porque no puedes anular un método interno
   clase pública ChildOutside: Parent
   {
   }

   prueba de clase pública 
   { 

     // Muy bien
     padre privado _parent;

     prueba pública ()
     {
       //Sigo bien
       _parent = new ChildWithin ();
     }
   }
 }

Como puede ver, de hecho permite que alguien use la clase para padres sin poder heredar.

Este ejemplo contiene dos archivos: Assembly1.cs y Assembly2.cs. El primer archivo contiene una clase base interna, BaseClass. En el segundo archivo, un bash de instanciar BaseClass producirá un error.

 // Assembly1.cs // compile with: /target:library internal class BaseClass { public static int intM = 0; } // Assembly1_a.cs // compile with: /reference:Assembly1.dll class TestAccess { static void Main() { BaseClass myBase = new BaseClass(); // CS0122 } } 

En este ejemplo, use los mismos archivos que utilizó en el ejemplo 1 y cambie el nivel de accesibilidad de BaseClass a público . También cambie el nivel de accesibilidad del miembro IntM a interno . En este caso, puede instanciar la clase, pero no puede acceder al miembro interno.

 // Assembly2.cs // compile with: /target:library public class BaseClass { internal static int intM = 0; } // Assembly2_a.cs // compile with: /reference:Assembly1.dll public class TestAccess { static void Main() { BaseClass myBase = new BaseClass(); // Ok. BaseClass.intM = 444; // CS0117 } } 

fuente : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx

Un uso muy interesante de interno, con un miembro interno, por supuesto, limitado solo al ensamblado en el que se declara, es obtener funcionalidades de “amigo” hasta cierto punto. Un miembro amigo es algo que es visible solo para ciertas otras asambleas fuera de la asamblea en la que se declara. C # no tiene soporte incorporado para friend, sin embargo, el CLR sí lo tiene.

Puede usar InternalsVisibleToAttribute para declarar un ensamblado amigo, y todas las referencias dentro del ensamblaje amigo tratarán a los miembros internos de su ensamblaje declarante como públicos dentro del scope del ensamblado amigo. Un problema con esto es que todos los miembros internos son visibles; no puedes escoger y elegir

Un buen uso de InternalsVisibleTo es exponer varios miembros internos a un conjunto de prueba unitaria, eliminando así las necesidades de trabajos de reflexión complejos para probar a esos miembros. Todos los miembros internos visibles no son un gran problema, sin embargo, tomar este enfoque ensucia las interfaces de clase y potencialmente puede arruinar la encapsulación dentro del ensamblado de statement.

Cuando tiene métodos, clases, etc. que deben ser accesibles dentro del scope del ensamblado actual y nunca fuera de él.

Por ejemplo, un DAL puede tener un ORM pero los objetos no deben exponerse a la capa de negocios. Toda interacción debe hacerse a través de métodos estáticos y pasar los parámetros requeridos.

Reducción de ruido, cuantos menos tipos exponga, más simple será su biblioteca. Tamper proofing / Security es otro (aunque Reflection puede ganarle).

Como regla general, hay dos tipos de miembros:

  • superficie pública : visible desde un ensamblaje externo (público, protegido y protegido interno): no se confía al llamador, por lo que se necesita validación de parámetros, documentación de métodos, etc.
  • superficie privada : no visible desde un ensamblaje externo (privado e interno, o clases internas): el que llama es generalmente de confianza, por lo que se puede omitir la validación de parámetros, la documentación de métodos, etc.

Las clases internas le permiten limitar la API de su ensamblaje. Esto tiene beneficios, como hacer que su API sea más simple de entender.

Además, si existe una falla en su ensamblaje, hay menos posibilidades de que el arreglo introduzca un cambio de rotura. Sin clases internas, tendría que asumir que cambiar los miembros públicos de cualquier clase sería un cambio radical. Con las clases internas, puede suponer que la modificación de sus miembros públicos solo rompe la API interna del ensamblado (y cualquier ensamblaje al que se haga referencia en el atributo InternalsVisibleTo).

Me gusta tener encapsulación en el nivel de clase y en el nivel de ensamblaje. Hay algunos que no están de acuerdo con esto, pero es bueno saber que la funcionalidad está disponible.

Tengo un proyecto que utiliza LINQ-to-SQL para el back-end de datos. Tengo dos espacios de nombres principales: Biz y Data. El modelo de datos LINQ vive en Datos y está marcado como “interno”; el espacio de nombres Biz tiene clases públicas que envuelven las clases de datos LINQ.

Entonces están Data.Client y Biz.Client ; este último expone todas las propiedades relevantes del objeto de datos, por ejemplo:

 private Data.Client _client; public int Id { get { return _client.Id; } set { _client.Id = value; } } 

Los objetos Biz tienen un constructor privado (para forzar el uso de métodos de fábrica) y un constructor interno que se ve así:

 internal Client(Data.Client client) { this._client = client; } 

Eso puede ser utilizado por cualquiera de las clases de negocios en la biblioteca, pero el front-end (IU) no tiene forma de acceder directamente al modelo de datos, asegurando que la capa de negocios siempre actúe como intermediario.

Esta es la primera vez que uso mucho internal , y está resultando bastante útil.

Hay casos en los que tiene sentido hacer internal miembros de las clases. Un ejemplo podría ser si desea controlar cómo se instancian las clases; supongamos que proporciona algún tipo de fábrica para crear instancias de la clase. Puede hacer que el constructor sea internal , para que la fábrica (que reside en el mismo ensamblaje) pueda crear instancias de la clase, pero el código fuera de ese ensamblaje no puede.

Sin embargo, no puedo ver ningún punto para hacer que las clases o los miembros sean internal sin razones específicas, tan poco como tiene sentido hacerlos public o private sin razones específicas.

lo único que he usado la palabra clave interna es el código de verificación de licencia en mi producto 😉

Un uso de la palabra clave interna es limitar el acceso a las implementaciones concretas del usuario de su ensamblado.

Si tiene una fábrica u otra ubicación central para construir objetos, el usuario de su ensamblaje solo necesita tratar con la interfaz pública o la clase base abstracta.

Además, los constructores internos le permiten controlar dónde y cuándo se crea una instancia de una clase que de otro modo sería pública.

¿Qué hay de este: por lo general, se recomienda no exponer un objeto de la Lista a los usuarios externos de un conjunto, en lugar de exponer un IEnumerable. Pero es mucho más fácil usar un objeto List dentro del ensamblaje, porque obtienes la syntax de la matriz y todos los demás métodos de la lista. Por lo tanto, normalmente tengo una propiedad interna que expone una lista para usar dentro del ensamblaje.

Los comentarios son bienvenidos sobre este enfoque.

Tenga en cuenta que cualquier clase definida como public aparecerá automáticamente en el intellisense cuando alguien mire el espacio de nombres de su proyecto. Desde la perspectiva de API, es importante mostrarle a los usuarios de su proyecto las clases que pueden usar. Use la palabra clave internal para ocultar cosas que no deberían ver.

Si su Big_Important_Class para el Proyecto A está diseñada para ser utilizada fuera de su proyecto, entonces no debe marcarla internal .

Sin embargo, en muchos proyectos, a menudo tendrás clases que solo están destinadas a ser utilizadas dentro de un proyecto. Por ejemplo, puede tener una clase que contenga los argumentos para una invocación de subprocesos parametrizada. En estos casos, debe marcarlos como internal por protegerse de un cambio de API no deseado en el futuro.

La idea es que cuando diseñe una biblioteca, solo las clases que están destinadas a ser utilizadas desde el exterior (por los clientes de su biblioteca) deben ser públicas. De esta manera puedes ocultar clases que

  1. Es probable que cambie en versiones futuras (si fueran públicas, romperías el código del cliente)
  2. Son inútiles para el cliente y pueden causar confusión
  3. No son seguros (por lo que un uso inapropiado podría dañar tu biblioteca bastante)

etc.

Si está desarrollando soluciones internas que usar elementos internos no es tan importante, supongo, porque generalmente los clientes tendrán contacto constante con usted y / o tendrán acceso al código. Sin embargo, son bastante críticos para los desarrolladores de bibliotecas.

Cuando tiene clases o métodos que no encajan limpiamente en el Paradigma orientado a objetos, que hacen cosas peligrosas, que necesitan ser llamados desde otras clases y métodos bajo su control, y que no quiere que nadie más use .

 public class DangerousClass { public void SafeMethod() { } internal void UpdateGlobalStateInSomeBizarreWay() { } } 
    Intereting Posts