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


420

¿Podría explicar cuál es el uso práctico de la internalpalabra clave en C #?

Sé que el internalmodificador limita el acceso al ensamblaje actual, pero ¿cuándo y en qué circunstancias debo usarlo?

Respuestas:


379

Utilidad o clases / métodos auxiliares a los que le gustaría acceder desde muchas otras clases dentro del mismo ensamblado, pero que desea garantizar que el código en otros ensamblados no pueda acceder.

Desde MSDN (a través de archive.org):

Un uso común del acceso interno es 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 construir interfaces gráficas de usuario podría proporcionar clases de Control y Formulario que cooperen usando 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 InternalsVisibleToatributo de nivel de ensamblaje para crear ensamblajes "amigos" que tengan acceso especial a las clases internas del ensamblado de destino.

Esto puede ser útil para la creación de conjuntos de prueba de unidad que luego se les permite llamar a los miembros internos del conjunto para que se prueben. Por supuesto, a ningún otro ensamblado se le otorga este nivel de acceso, por lo que cuando libera su sistema, se mantiene la encapsulación.


8
El atributo InternalsVisibleTo es de gran ayuda. Gracias.
erict

33
Tengo la sensación de que desde el momento en que necesita interna, hay algo mal en el diseño en alguna parte. En mi humilde opinión, uno debería poder utilizar OO puro para resolver todos los problemas. En este mismo momento, estoy considerando aproximadamente el uso número un millón de interno en la fuente de .NET Framework, lo que me impide aprovechar las cosas que usan ellos mismos. Mediante el uso de internos, crearon un monolito que es superficialmente modular, pero se descompone cuando intenta aislar cosas. Además, la seguridad no es un argumento, ya que hay reflejo del rescate.
Mueca de desesperación

26
@GrimaceofDespair: Déjame agregar un comentario diagreeing aquí. Personalmente, veo interna como una versión muy útil del modificador "público" en grandes soluciones multiproyecto. Agregar este modificador a una clase en mi subproyecto prohíbe el "uso aleatorio" de esta clase por parte de otros subproyectos en la solución y me permite forzar el uso adecuado de mi biblioteca. Si necesito refactorizar más adelante, no tendré que preguntarme "espera, pero ¿por qué ese tipo fue y creó una instancia de la clase Point que solo necesitaba para mis propios fines dentro de este cálculo en particular"?
KT.

3
Para decirlo de otra manera, si todos confiaran en los componentes internos del SmtpClient, y en un momento se descubriera un error allí, .NET tendría serios problemas para solucionar ese error, tal vez incluso mantenerlo durante mucho tiempo. Recuerdo que una vez leí un artículo divertido que describía la longitud a la que los desarrolladores de Windows tenían que ir para mantener la "compatibilidad sin errores" con todos los programas más antiguos que utilizaban varias características y errores internos. Repetir este error con .NET no tiene sentido.
KT.

13
Creo que interno es MUCHO más útil que público. El único momento en que usa public es cuando explícitamente dice "Aquí, usuario: estoy proporcionando una función útil para que la use". Si está escribiendo una aplicación en lugar de una biblioteca de usuario, todo debería ser interno porque no está tratando de dar a las personas ninguna función para llamar externamente. Si está escribiendo una biblioteca de usuario, solo las funciones útiles que espera dar al usuario, mantener, mantener y mantener hasta el final de los tiempos deben ser públicas. De lo contrario, hazlo interno.
Darrell Plank

85

Si Bob necesita BigImportantClass, entonces Bob debe hacer que las personas que poseen el proyecto A se inscriban para garantizar que BigImportantClass se escriba para satisfacer sus necesidades, se pruebe para garantizar que satisfaga sus necesidades, se documente como un cumplimiento de sus necesidades y que ese proceso se implementará para garantizar que nunca se cambie y 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 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.


55
Tiene mucho sentido. Solo desearía tener la capacidad de anular a los miembros internos, porque aquí todos somos adultos con consentimiento.
cwallenpoole

66
@EricLippert No es exactamente lo que quise decir. Si heredo el código compilado que tiene "interno", estoy atascado, ¿verdad? No hay forma de decirle simplemente al compilador "No, ese otro desarrollador te mintió. Deberías confiar en mí" a menos que use la reflexión.
cwallenpoole

11
@cwallenpoole: si está usando código escrito por mentirosos que escriben código que no le gusta, deje de usar su código y escriba su propio código que más le guste.
Eric Lippert

2
@EricLippert Se suponía que eso era una lengua en la mejilla, no era mi intención ofender. (En su mayoría solo intentaba confirmar que soy SOL en tales casos).
cwallenpoole

55
@cwallenpoole: no me ofende en lo más mínimo. Te estoy ofreciendo buenos consejos si te encuentras atrapado en esa desafortunada situación.
Eric Lippert

60

Otra razón para usar interno es si ofuscas tus binarios. El ofuscador sabe que es seguro codificar el nombre de la 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.


44
Pff. Mirar el código de bytes con un desensamblador aún dejará bastante claro lo que hace el código de todos modos. Los ofuscadores son apenas un disuasivo leve para cualquiera que realmente intente meterse en lo interno. Nunca escuché de un pirata informático que se detuvo solo porque no obtuvo nombres de funciones útiles.
Nyerguds

28
así que no cierras la puerta cuando duermes, porque nunca has oído hablar de un ladrón detenido por una cerradura
Sergio

44
Es por eso que codifico los nombres de clases y variables en el código fuente. Es posible que pueda descubrir qué es un PumpStateComparator, pero ¿puede adivinar qué es un LpWj4mWE? #jobsecurity (no hago esto, y por favor no hagas esto, gente de internet)
Slothario

32

Si está escribiendo una DLL que encapsula una tonelada de funcionalidades complejas en una API pública simple, entonces se utiliza "interno" en los miembros de la clase que no deben exponerse públicamente.

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


20

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

Cuando tiene una biblioteca basada en C / C ++ que desea importar, puede importar estas funciones como funciones estáticas de una clase y hacerlas internas, de modo que su usuario solo tenga acceso a su contenedor y no a la API original para que no pueda meterse con cualquier cosa. Como las funciones son estáticas, puede usarlas en todas partes del ensamblaje, para las múltiples clases de envoltura que necesita.

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


12

Siendo impulsado por la regla de "usar el modificador tan estricto como pueda", uso el interno en todas partes donde necesito acceder, por ejemplo, al método de otra clase hasta que explícitamente necesito acceder a él desde otro ensamblado.

Como la interfaz de ensamblaje suele ser más estrecha que la suma de sus interfaces de clases, hay muchos lugares donde la uso.


3
Si "usa el modificador tan estricto como puede", ¿por qué no privado entonces?
R. Martinho Fernandes

66
Dado que privado es demasiado estricto cuando se debe acceder a las propiedades / métodos de clase fuera de la clase, y los casos en que se debe acceder desde otro ensamblado, bly no es tan frecuente.
Ilya Komakhin

11

Encuentro interno que está muy usado en exceso. realmente no debería exponer ciertas funciones solo a ciertas clases que no 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 usar 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 otro desarrollador puede ser acusado de construir otra clase en el mismo ensamblado que el suyo. Tener elementos internos disminuye la claridad de la abstracción y puede causar problemas si se usa incorrectamente. Sería el mismo problema que si lo hicieras público. La otra clase que está desarrollando el otro desarrollador sigue siendo un consumidor, al igual que cualquier clase externa. La abstracción de clases y la encapsulación no son solo para protección para / 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 otra parte del ensamblaje y marcarlo como interno de todos modos, aunque no lo necesiten en ese momento. Otro desarrollador puede pensar que está ahí para tomar. Por lo general, desea marcar privado hasta que tenga una necesidad definitiva.

Pero algo de esto puede ser subjetivo, y no digo que nunca deba usarse. Solo use cuando sea necesario.


A menudo construyo objetos de dominio para los que tienen miembros que deberían ser públicos para otros objetos de dominio dentro del mismo ensamblado. Sin embargo, estos mismos miembros NO deberían estar disponibles para el código del cliente que está fuera del ensamblado. Para situaciones como esta, 'interno' es muy apropiado.
Rock Anthony Johnson

8

Vi uno interesante el otro día, tal vez una 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 otra clase vea una clase abstracta pero que no desea que alguien pueda heredar de ella. Sellado no funcionará porque es abstracto por una razón, otras clases en ese ensamblado heredan de él. Privado no funcionará porque es posible que desee declarar una clase Parent en algún lugar del otro ensamblado.

Base de espacio de nombres.
{
  clase abstracta pública Padre
  {
    vacío interno abstracto SomeMethod ();
  }

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

espacio de nombres Otro.
{
  // Kaboom, porque no puedes anular un método interno
  Clase pública Niño Fuera: Padre
  {
  }

  Prueba de clase pública 
  { 

    // bien
    Padres privados _ padres;

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

Como puede ver, efectivamente permite que alguien use la clase Parent sin poder heredar.


7

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 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 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


6

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

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


6

Un uso muy interesante de interno, con el miembro interno limitado, por supuesto, solo al conjunto en el que se declara, es obtener la funcionalidad de "amigo" hasta cierto punto. Un miembro amigo es algo que solo es visible para ciertos otros conjuntos fuera del conjunto en el que se declara. C # no tiene soporte incorporado para amigo, sin embargo, el CLR sí.

Puede usar InternalsVisibleToAttribute 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 conjunto de prueba de unidad, eliminando así la necesidad de complejos trabajos de reflexión para probar esos miembros. Que todos los miembros internos sean visibles no es un gran problema, sin embargo, adoptar este enfoque ensucia mucho las interfaces de su clase y puede arruinar la encapsulación dentro del conjunto de declaración.


5

Como regla general, hay dos tipos de miembros:

  • superficie pública : visible desde un ensamblaje externo (público, protegido y protegido internamente): 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): la persona que llama generalmente es confiable, por lo que se puede omitir la validación de parámetros, la documentación del método, etc.

4

Reducción de ruido, cuantos menos tipos expongas, más simple será tu biblioteca. La prueba de manipulación / seguridad es otra (aunque Reflection puede ganar contra ella).


4

Las clases internas le permiten limitar la API de su ensamblaje. Esto tiene beneficios, como hacer que su 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, debería asumir que cambiar los miembros públicos de cualquier clase sería un cambio radical. Con las clases internas, puede suponer que modificar sus miembros públicos solo rompe la API interna del ensamblado (y cualquier ensamblado al que se haga referencia en el atributo InternalsVisibleTo).

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


2

Tengo un proyecto que usa 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 se ajustan a las clases de datos LINQ.

Entonces hay 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;
}

Puede ser utilizado por cualquiera de las clases de negocios en la biblioteca, pero el front-end (UI) 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 realmente uso internalmucho, y está resultando bastante útil.


2

Hay casos en los que tiene sentido formar miembros de clases internal. 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 el constructor 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.

Sin embargo, no veo ningún punto en hacer clases o miembros internalsin razones específicas, tan poco como tenga sentido hacerlas public, o privatesin razones específicas.


1

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


1

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

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 instancia una clase pública.


1

Qué tal este: por lo general, se recomienda que no exponga un objeto List a usuarios externos de un ensamblado, sino que exponga un IEnumerable. Pero es mucho más fácil usar un objeto List dentro del ensamblado, porque obtienes la sintaxis de la matriz y todos los demás métodos List. Por lo tanto, normalmente tengo una propiedad interna que expone una Lista para usar dentro del ensamblaje.

Se agradecen los comentarios sobre este enfoque.


@Samik: ¿podría explicarnos: "por lo general, se recomienda que no exponga un objeto List a usuarios externos de un ensamblado, sino que exponga un IEnumerable". ¿Por qué se prefiere innumerable?
Howiecamp

1
@Howiecamp: Principalmente porque IEnumerable brinda un acceso de solo lectura a la lista, donde, como si expones la lista directamente, los clientes pueden modificarla (como eliminar elementos, etc.), lo que puede ser involuntario.
Samik R

Además, exponer una Lista o una Lista IL crea un contrato que siempre permitirá (por ejemplo) un acceso aleatorio rápido a los datos. Si algún día decide cambiar su método para usar otra colección, o ninguna colección (rendimiento de rendimiento), tendrá que crear una Lista solo para devolver un valor, o romper el contrato cambiando el tipo de retorno del método. El uso de un tipo de retorno menos específico le brinda flexibilidad.

1

Tenga en cuenta que cualquier clase definida como publicse mostrará automáticamente en el intellisense cuando alguien mira el espacio de nombres de su proyecto. Desde una perspectiva API, es importante mostrar solo a los usuarios de su proyecto las clases que pueden usar. Use la internalpalabra clave para ocultar cosas que no deberían ver.

Si su Big_Important_Classproyecto A está destinado a usarse fuera de su proyecto, entonces no debe marcarlo internal.

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


Entonces, ¿por qué no usar privado en su lugar?
Prisionero CERO

1
La privatepalabra clave solo se puede usar en clases o estructuras que se definen dentro de otra clase o estructura. Una clase definida dentro de un espacio de nombres solo se puede declarar publico internal.
Matt Davis el

1

La idea es que, cuando diseñe una biblioteca, solo las clases 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 cambien en futuras versiones (si fueran públicas, rompería el código del cliente)
  2. Son inútiles para el cliente y pueden causar confusión.
  3. No son seguros (por lo que el uso incorrecto podría dañar su biblioteca bastante mal)

etc.

Supongo que si está desarrollando soluciones internas, el uso de 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.


-7

Cuando tiene clases o métodos que no encajan perfectamente en el Paradigma orientado a objetos, que hacen cosas peligrosas, que deben ser llamadas desde 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() { }
}

¿Qué estás tratando de decir? No está muy claro. Al menos no para mí.
pqsk

De acuerdo con @pqsk
Anynomous Khan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.