¿Por qué usar un método público en una clase interna?

250

Hay mucho código en uno de nuestros proyectos que se ve así:

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

¿Hay alguna razón explícita para hacer esto que no sea "es más fácil hacer público el tipo más tarde?"

Sospecho que solo importa en casos extremos muy extraños (reflejo en Silverlight) o en absoluto.

bopapa_1979
fuente
1
En mi experiencia, esa es la práctica normal.
phoog
2
@Phoog, OK. pero ¿por qué es la práctica normal? ¿Quizás es más fácil que cambiar un montón de métodos a internos? Solución rápida -> marque el tipo interno en su lugar?
bopapa_1979
Eso es lo que siempre he asumido. Sin embargo, no tengo un análisis bien pensado, por eso no publiqué una respuesta.
phoog
2
La respuesta de Eric Lippert resume muy bien mi pensamiento, y también trae algo que acechaba en mi cerebro tratando de encontrar una salida: las implementaciones implícitas de los miembros de la interfaz deben ser públicas.
phoog
¿No es ese método igual return s + "Foo";? Al +operador no le importan las cadenas nulas o vacías.
Mikkel R. Lund

Respuestas:

418

ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog en septiembre de 2014 . Gracias por la gran pregunta!

Existe un debate considerable sobre esta cuestión, incluso dentro del propio equipo compilador.

En primer lugar, es sabio entender las reglas. Un miembro público de una clase o estructura es un miembro accesible para cualquier cosa que pueda acceder al tipo que lo contiene . Entonces, un miembro público de una clase interna es efectivamente interno.

Entonces, dada una clase interna, ¿deberían sus miembros a los que desea acceder en la asamblea ser marcados como públicos o internos?

Mi opinión es: marcar tales miembros como público.

Utilizo "public" para significar "este miembro no es un detalle de implementación". Un miembro protegido es un detalle de implementación; hay algo al respecto que será necesario para que una clase derivada funcione. Un miembro interno es un detalle de implementación; Algo más interno de esta asamblea necesita el miembro para funcionar correctamente. Un miembro público dice "este miembro representa la funcionalidad clave y documentada proporcionada por este objeto".

Básicamente, mi actitud es: supongamos que decidí convertir esta clase interna en una clase pública. Para hacer eso, quiero cambiar exactamente una cosa : la accesibilidad de la clase. Si convertir una clase interna en una clase pública significa que también tengo que convertir a un miembro interno en un miembro público, entonces ese miembro era parte de la superficie pública de la clase, y debería haber sido público en primer lugar.

Otras personas no están de acuerdo. Hay un contingente que dice que quieren poder echar un vistazo a la declaración de un miembro y saber de inmediato si solo se llamará desde el código interno.

Desafortunadamente, eso no siempre funciona bien; por ejemplo, una clase interna que implementa una interfaz interna todavía tiene que tener los miembros implementadores marcados como públicos, porque son parte de la superficie pública de la clase .

Eric Lippert
fuente
11
Me gusta esta respuesta ... encaja bien con mi actitud hacia la escritura de código que se auto documenta en la mayor medida posible, y el alcance es una excelente manera de transmitir la intención, especialmente si los otros miembros de su equipo entienden su convención.
bopapa_1979
10
+1 por decir lo que quería decir pero formularlo mucho mejor de lo que pude (también por mencionar el ángulo de la interfaz, aunque observo que solo es cierto para implementaciones de miembros implícitos).
phoog
2
La parte de la interfaz es importante: es imposible implementar un método de interfaz que utilice una declaración de interfaz interna o explícita. Entonces la palabra público está sobrecargada con dos significados.
Michael Stum
8
Desde una perspectiva idealógica, su argumento a favor publices muy convincente. Sin embargo, creo que "no siempre funciona bien ..." es especialmente poderoso. La pérdida de consistencia devalúa cualquier beneficio "de un vistazo". Usar internalde esta manera significa construir deliberadamente intuiciones que a veces están mal. Tener una intuición mayormente correcta es IMO, algo horrible para programar.
Brian
3
Cita importante de su publicación de blog: "Mi consejo es discutir el problema entre su equipo, tomar una decisión y luego atenerse a ella".
bopapa_1979
17

Si la clase es internal, no importa desde el punto de vista de la accesibilidad si marca un método internalo no public. Sin embargo, sigue siendo bueno usar el tipo que usarías si la clase lo fuera public.

Si bien algunos han dicho que esto facilita las transiciones de internala public. También sirve como parte de la descripción del método. Internallos métodos generalmente se consideran inseguros para el acceso sin restricciones, mientras que los publicmétodos se consideran (en su mayoría) juegos gratuitos.

Al usar internalo publiccomo lo haría en una publicclase, se asegura de comunicar qué estilo de acceso se espera, al tiempo que facilita el trabajo requerido para hacer la clase publicen el futuro.

Guvante
fuente
Tiendo a limitar todo en la mayor medida posible al crear una buena API y permitir que mi consumidor objetivo haga lo que tengo la intención de hacer. También estoy contigo para marcar al miembro como lo haría si la clase fuera pública. Más específicamente, solo marcaría a cualquier miembro público si lo considero siempre seguro. De lo contrario, alguien más que marque la clase pública más tarde (sucede) podría exponer a miembros no seguros.
bopapa_1979
@Eric: Si quiere renunciar a determinar la seguridad de un método hasta que esté listo, está bien, pero me refería solo a los métodos considerados seguros, en cuyo caso no hay exposición.
Guvante
10

A menudo marco mis métodos en clases internas como públicas en lugar de internos como a) realmente no importa yb) Uso interno para indicar que el método es interno a propósito (hay alguna razón por la que no quiero exponer esto método en una clase pública. Por lo tanto, si tengo un método interno realmente tengo que entender la razón por la cual es interno antes de cambiarlo a público, mientras que si estoy tratando con un método público en una clase interna realmente tengo que pensar por qué La clase es interna en lugar de por qué cada método es interno.

Mondiamond ǤeezeƦ
fuente
Gracias por la respuesta. Sin embargo, tiendo a insistir obstinadamente que "todo" importa al codificar :)
bopapa_1979
1
Tienes razón Eric, fue un comentario un poco descartable. Espero que el resto de mi respuesta sea de alguna utilidad. Creo que la respuesta de Eric Lippert es lo que estaba tratando de expresar, pero me lo explicó mucho mejor.
mondiamond ǤeezeƦ
8

Sospecho que "¿es más fácil hacer público el tipo más tarde?" Lo es.

Las reglas de alcance significan que el método solo será visible como internal, por lo que realmente no importa si los métodos están marcados publico no.internal .

Una posibilidad que viene a la mente es que la clase era pública y luego se cambió a internaly el desarrollador no se molestó en cambiar todos los modificadores de accesibilidad del método.

Oded
fuente
1
+1 por clavar el obvio escenario de "clase pública se convirtió en interno".
bopapa_1979
8

En algunos casos, también puede ser que el tipo interno implemente una interfaz pública, lo que significaría que cualquier método definido en esa interfaz aún debería declararse como público.

Trevor Pilley
fuente
2

Es lo mismo, el método público se marcará realmente como interno ya que está dentro de una clase interna, pero tiene una ventaja (como adivinó), si desea marcar la clase como pública, debe cambiar menos código.

Mario Corchero
fuente
2
ya hay una pregunta relacionada: stackoverflow.com/questions/711361/…
Mario Corchero
0

internaldice que solo se puede acceder al miembro desde el mismo ensamblado. Otras clases en esa asamblea pueden acceder al internal publicmiembro, pero no sería capaz de acceder a una privateo protectedmiembro, internalo no.

Ryan P
fuente
Pero si los métodos se marcaran en internallugar de public, su visibilidad no cambiaría. Creo que eso es lo que pregunta el OP.
Oded
1
@zapthedingbat: la publicación es objetivamente correcta, pero ¿realmente está respondiendo la pregunta?
Oded
1
Correcto, la pregunta es POR QUÉ marcarlos de esa manera. Entiendo los conceptos básicos del alcance. ¡Gracias por el intento, sin embargo!
bopapa_1979
0

En realidad luché con esto hoy. Hasta ahora, habría dicho que todos los métodos deberían estar marcados internalsi la clase fuera internaly hubiera considerado cualquier otra cosa simplemente una mala codificación o pereza, especialmente en el desarrollo empresarial; Sin embargo, tuve que subclasificar una publicclase y anular uno de sus métodos:

internal class SslStreamEx : System.Net.Security.SslStream
{
    public override void Close()
    {
        try
        {
            // Send close_notify manually
        }
        finally
        {
            base.Close();
        }
    }
}

El método DEBE ser publicy me hizo pensar que realmente no hay ningún punto lógico para establecer métodos comointernal menos que realmente lo sean, como dijo Eric Lippert.

Hasta ahora nunca me he detenido a pensar en ello, simplemente lo acepté, pero después de leer la publicación de Eric realmente me hizo pensar y después de mucho pensarlo tiene mucho sentido.

Tormenta
fuente
0

Hay una diferencia En nuestro proyecto, hemos realizado muchas clases internas, pero hacemos pruebas unitarias en otro ensamblaje y en nuestra información de ensamblaje utilizamos InternalsVisibleTo para permitir que el ensamblaje UnitTest llame a las clases internas. He notado que si la clase interna tiene un constructor interno, no podemos crear una instancia usando Activator.CreateInstance en el ensamblaje de prueba de la unidad por alguna razón. Pero si cambiamos el constructor a public pero la clase sigue siendo interna, funciona bien. Pero supongo que este es un caso muy raro (como dijo Eric en la publicación original: Reflexión).

Farrah Jiang
fuente
0

Creo que tengo una opinión adicional sobre esto. Al principio, me preguntaba cómo tiene sentido declarar algo al público en una clase interna. Luego terminé aquí, leyendo que podría ser bueno si luego decides cambiar la clase a público. Cierto. Entonces, un patrón se formó en mi mente: si no cambia el comportamiento actual, entonces sea permisivo y permita cosas que no tienen sentido (y no duelen) en el estado actual del código, pero luego lo haría, si usted Cambiar la declaración de la clase.

Me gusta esto:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

De acuerdo con el "patrón" que he mencionado anteriormente, esto debería estar perfectamente bien. Sigue la misma idea. Se comporta como un privatemétodo, ya que no puede heredar la clase. Pero si elimina la sealedrestricción, sigue siendo válida: las clases heredadas pueden ver este método, que es absolutamente lo que quería lograr. Pero recibes una advertencia: CS0628oCA1047 . Ambos se trata de no declarar protectedmiembros en una sealedclase. Además, he encontrado un acuerdo total, sobre eso no tiene sentido: advertencia de 'Miembro protegido en clase sellada' (una clase singleton)

Entonces, después de esta advertencia y la discusión vinculada, he decidido hacer que todo sea interno o menos, en una clase interna, porque se ajusta más a ese tipo de pensamiento, y no mezclamos diferentes "patrones".

Hix
fuente
Drásticamente prefiero hacerlo de la manera (o al menos por las razones), dijo Eric Lippert. Definitivamente vale la pena leer su artículo.
bopapa_1979