niveles - Usos prácticos de la palabra clave "interna" en C#
protected internal in c# example (22)
¿Podría explicar 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?
¿Qué tal esto? Normalmente, se recomienda que no exponga un objeto de Lista a usuarios externos de un ensamblaje, sino que exponga un IEnumerable. Pero es mucho más fácil usar un objeto de Lista dentro del ensamblaje, porque obtiene la sintaxis de la matriz y todos los demás métodos de Lista. Por lo tanto, normalmente tengo una propiedad interna que expone una lista para usarla dentro del ensamblaje.
Los comentarios son bienvenidos acerca de este enfoque.
Al ser guiado por la regla de "usar lo más estricto posible", uso el método interno en todas partes donde necesito acceder, por ejemplo, el método de otra clase hasta que explícitamente necesito acceder a él desde otro conjunto.
Como la interfaz de ensamblaje suele ser más estrecha que la suma de las interfaces de clase, hay muchos lugares donde la uso.
Clases / métodos de utilidad o auxiliares a los que le gustaría acceder desde muchas otras clases dentro del mismo ensamblaje, pero a la que desea garantizar que no pueda acceder el código de otros ensamblajes.
Desde MSDN :
Un uso común del acceso interno es en el desarrollo basado en componentes porque permite que un grupo de componentes coopere de manera privada sin estar expuesto al resto del código de la aplicación. Por ejemplo, un marco para la creación de interfaces gráficas de usuario podría proporcionar clases de control y formulario que cooperen utilizando miembros con acceso interno. Dado que estos miembros son internos, no están expuestos al código que utiliza 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 otorga acceso especial a las clases internas del ensamblaje de destino.
Esto puede ser útil para la creación de ensamblajes de prueba unitaria que luego se les permite llamar a los miembros internos del ensamblaje a probar. Por supuesto, a ningún otro conjunto se le otorga este nivel de acceso, por lo que cuando libera su sistema, se mantiene la encapsulación.
Como regla de oro hay dos tipos de miembros:
- superficie pública : visible desde un ensamblaje externo (público, protegido y protegido interno): la persona que llama no es confiable, por lo que se necesita validación de parámetros, documentación del método, etc.
- superficie privada : no visible desde un ensamblaje externo (privado e interno, o clases internas): el llamador generalmente es de confianza, por lo que se puede omitir la validación de parámetros, la documentación del método, etc.
Cuando tenga métodos, clases, etc. que deban ser accesibles dentro del alcance del conjunto actual y nunca fuera de él.
Por ejemplo, un DAL puede tener un ORM, pero los objetos no deben estar expuestos a la capa empresarial; toda interacción debe realizarse a través de métodos estáticos y pasar los parámetros necesarios.
Cuando tiene clases o métodos que no encajan limpiamente en el Paradigma Orientado a Objetos, que hacen cosas peligrosas, que deben llamarse de otras clases y métodos bajo su control, y que no desea que nadie más use .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}
Encuentro interno para ser muy usado en exceso. realmente no debería estar exponiendo cierta función solo a ciertas clases que no lo haría a otros consumidores.
Esto en mi opinión rompe la interfaz, rompe la abstracción. Esto no quiere decir que nunca se debe usar, pero una mejor solución es refactorizar a una clase diferente o ser usado de una manera diferente si es posible. Sin embargo, esto puede no ser siempre posible.
La razón por la que puede causar problemas es que a otro desarrollador se le puede encargar la creación de otra clase en el mismo conjunto que el suyo. Tener elementos internos disminuye la claridad de la abstracción y puede causar problemas si se utiliza incorrectamente. Sería el mismo problema que si lo hicieras público. La otra clase que está construyendo 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 clases externas, sino para todas y cada una de las clases.
Otro problema es que muchos desarrolladores pensarán que pueden necesitar usarlo en cualquier otro lugar del ensamblaje y marcarlo como interno de todos modos, aunque no lo necesiten en ese momento. Otro desarrollador puede pensar que está ahí para ser tomado. Por lo general, usted desea marcar en privado hasta que tenga una necesidad definitiva.
Pero algo de esto puede ser subjetivo, y no estoy diciendo que nunca deba usarse. Sólo use cuando sea necesario.
Este ejemplo contiene dos archivos: Assembly1.cs y Assembly2.cs. El primer archivo contiene una clase base interna, BaseClass. En el segundo archivo, un intento de crear una instancia de 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 usó 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 crear una instancia de 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
Hay casos en los que tiene sentido hacer miembros de clases internal
. Un ejemplo podría ser si desea controlar cómo se crean instancias de las clases; digamos que proporciona algún tipo de fábrica para crear instancias de la clase. Puede hacer que el constructor sea internal
, de modo 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 veo ningún punto en 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.
La idea es que cuando esté diseñando una biblioteca, solo las clases destinadas a uso externo (por parte de los clientes de su biblioteca) deberían ser públicas. De esta manera puedes ocultar clases que
- Es probable que cambien en futuras versiones (si fueran públicas, se rompería el código del cliente)
- Son inútiles para el cliente y pueden causar confusión.
- No son seguros (por lo que un uso inadecuado podría dañar tu biblioteca bastante mal)
etc.
Supongo que si está desarrollando soluciones internas que utilizar elementos internos no es tan importante, porque generalmente los clientes tendrán contacto constante con usted y / o acceso al código. Sin embargo, son bastante críticos para los desarrolladores de bibliotecas.
La palabra clave interna se usa mucho cuando se crea un contenedor sobre un código no administrado.
Cuando tiene una biblioteca basada en C / C ++ que desea importar mediante DllImport, puede importar estas funciones como funciones estáticas de una clase y hacerlas internas, de modo que su usuario solo tenga acceso a su envoltorio y no a la API original, por lo que no puede meterse con cualquier cosa. Como las funciones son estáticas, puede usarlas en cualquier parte del ensamblaje, para las múltiples clases de envoltorios que necesita.
Puede echar un vistazo a Mono.Cairo, es una envoltura alrededor de la biblioteca de El Cairo que utiliza este enfoque.
Las clases internas le permiten limitar la API de su ensamblaje. Esto tiene beneficios, como hacer que tu API sea más fácil de entender.
Además, si existe un error en su ensamblaje, hay menos posibilidades de que la solución introduzca un cambio importante. Sin clases internas, tendría que asumir que cambiar a los miembros públicos de cualquier clase sería un cambio importante. Con las clases internas, puede suponer que la modificación de sus miembros públicos solo rompe la API interna del ensamblaje (y cualquier ensamblado al que se hace referencia en el atributo InternalsVisibleTo).
Me gusta tener la encapsulación en el nivel de clase y en el nivel de montaje. Hay algunos que no están de acuerdo con esto, pero es bueno saber que la funcionalidad está disponible.
Lo único en lo que he usado la palabra clave interna es el código de verificación de licencia en mi producto ;-)
Otra razón para usar interno es si ofusca sus binarios. El ofuscador sabe que es seguro mezclar el nombre de la clase de cualquier clase interna, mientras que el nombre de las clases públicas no puede ser codificado, porque eso podría romper las referencias existentes.
Reducción de ruido, mientras menos tipos exponga, más simple será su biblioteca. La prueba de manipulación indebida / seguridad es otra (aunque Reflection puede ganar contra ella).
Si Bob necesita BigImportantClass, entonces 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 cumple con sus necesidades, se documenta que cumple 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, entonces no tiene que pasar por ese proceso, lo que ahorra presupuesto para el Proyecto A que pueden gastar en otras cosas.
El punto de lo interno no es que le dificulte la vida a Bob. Es que le permite controlar qué costosas promesas está haciendo el Proyecto A sobre las características, el tiempo de vida, la compatibilidad, etc.
Si está escribiendo una DLL que encapsula una tonelada de funcionalidades complejas en una simple API pública, entonces se usa "interno" en los miembros de la clase que no deben ser expuestos públicamente.
La complejidad de ocultación (también conocida como encapsulación) es el concepto principal de ingeniería de software de calidad.
Tenga en cuenta que cualquier clase definida como public
aparecerá automáticamente en la inteligencia cuando alguien mire el espacio de nombres de su proyecto. Desde la perspectiva de la API, es importante que solo muestre 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 usarse fuera de su proyecto, entonces no debe marcarlo como internal
.
Sin embargo, en muchos proyectos, a menudo tendrá clases que solo están diseñadas para ser utilizadas dentro de un proyecto. Por ejemplo, puede tener una clase que contenga los argumentos de una invocación de hilo parametrizada. En estos casos, debe marcarlos como internal
por el único motivo de protegerse de un cambio de API no deseado en el futuro.
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 de Biz tiene clases públicas que envuelven las clases de datos LINQ.
Así que hay Data.Client
, y Biz.Client
; el ú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 de Biz tienen un constructor privado (para forzar el uso de métodos de fábrica) y un constructor interno que se parece a esto:
internal Client(Data.Client client) {
this._client = client;
}
Cualquiera de las clases de negocios en la biblioteca puede utilizarlo, pero el front-end (UI) no tiene forma de acceder directamente al modelo de datos, lo que garantiza que la capa de negocios siempre actúe como un intermediario.
Esta es la primera vez que uso mucho internal
, y está resultando muy útil.
Un uso de la palabra clave interna es limitar el acceso a implementaciones concretas del usuario de su ensamblaje.
Si tiene una fábrica o alguna otra ubicación central para construir objetos, el usuario de su ensamblaje solo necesita lidiar 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 pública.
Un uso muy interesante de interno (el miembro interno, por supuesto, se limita solo al ensamblaje en el que se declara) es obtener cierta funcionalidad de "amigo". Un miembro amigo es algo que solo es visible para ciertos otros ensamblajes fuera del ensamblaje en el que se declara. C # no tiene soporte incorporado para amigo, sin embargo, el CLR sí lo hace.
Puede utilizar InternalsVisibleTo para declarar una asamblea de amigos, y todas las referencias dentro de la asamblea de amigos tratarán a los miembros internos de su asamblea declarante como públicos dentro del alcance de la asamblea de amigos. Un problema con esto es que todos los miembros internos son visibles; No puedes escoger y elegir.
Un buen uso para InternalsVisibleTo es exponer a varios miembros internos a un ensamblaje de prueba unitaria, eliminando así la necesidad de realizar complejas reflexiones para evaluar a esos miembros. El hecho de que todos los miembros internos sean visibles no es un problema, sin embargo, si se toma este enfoque, se arruinan las interfaces de clase y puede arruinar la encapsulación dentro del ensamblaje declarante.
Vi una interesante el otro día, tal vez semana, en un blog que no recuerdo. Básicamente no puedo tomar crédito por esto, pero pensé que podría tener alguna aplicación útil.
Digamos que desea que una clase abstracta sea vista por otro conjunto pero no desea que alguien pueda heredar de ella. Sellado no funcionará porque es abstracto por una razón, otras clases en ese ensamblaje sí lo heredan. Privado no funcionará porque es posible que desee declarar una clase principal en algún lugar del otro conjunto.
namespace Base.Assembly { public abstract class Parent { internal abstract void SomeMethod(); } //This works just fine since it''s in the same assembly. public class ChildWithin : Parent { internal override void SomeMethod() { } } } namespace Another.Assembly { //Kaboom, because you can''t override an internal method public class ChildOutside : Parent { } public class Test { //Just fine private Parent _parent; public Test() { //Still fine _parent = new ChildWithin(); } } }
Como puede ver, efectivamente le permite a alguien usar la clase de Padres sin poder heredar.