En lugar de preguntar por qué están privada, vamos a reformular la pregunta a esto:
- ¿Qué pasaría si los métodos parciales no eran privadas?
Considere este ejemplo sencillo:
public partial class Calculator
{
public int Divide(int dividend, int divisor)
{
try
{
return dividend/divisor;
}
catch (DivideByZeroException ex)
{
HandleException(ex);
return 0;
}
}
partial void HandleException(ArithmeticException ex);
}
Ignoremos por el momento la cuestión de por qué nos gustaría hacer de este un método parcial en contraposición a uno abstracto (voy a volver a eso) . Lo importante es que esto compila, y funciona correctamente, si el método HandleException
está implementado o no. Si nadie lo implementa, esto sólo se come la excepción y devuelve 0.
Ahora vamos a cambiar las reglas, decir que el método parcial podría ser protegido:
public partial class Calculator
{
// Snip other methods
// Invalid code
partial protected virtual void HandleException(ArithmeticException ex);
}
public class LoggingCalculator : Calculator
{
protected override virtual void HandleException(ArithmeticException ex)
{
LogException(ex);
base.HandleException(ex);
}
private void LogException(ArithmeticException ex) { ... }
}
Tenemos un poco de un problema aquí. Hemos "anulado" el método HandleException
, excepto que todavía no hay ningún método para anular. Y me refiero a que el método literalmente no existe, no está siendo compilado en absoluto.
¿Qué significa lo que nuestra base Calculator
invoca HandleException
? ¿Debería invocar el método derivado (reemplazado)? Si es así, ¿qué código emite el compilador para el método base HandleException
? ¿Debería convertirse en un método abstracto? Un método vacío? ¿Y qué sucede cuando el método derivado llama al base.HandleException
? ¿Se supone que esto no hace nada? Levanta un MethodNotFoundException
? Es realmente difícil seguir el principio de la menor sorpresa aquí; casi cualquier cosa que hagas será sorprendente.
O tal vez no ocurra nada cuando se invoca HandleException
, porque el método base no se implementó. Sin embargo, esto no parece muy intuitivo. Nuestra clase derivada se ha ido e implementado este método y la clase base se ha ido y ha sacado la alfombra de debajo sin que lo sepamos. Fácilmente puedo imaginarme a un desarrollador pobre que se tira de los pelos, incapaz de entender por qué su método reemplazado nunca se ejecuta.
O tal vez este código no debe compilar en absoluto o debe producir una advertencia. Pero esto tiene una serie de problemas propios. Lo que es más importante, rompe el contrato provisto por métodos parciales, que dice que descuidar la implementación de uno nunca debe dar como resultado un error de compilación. Tienes una clase base que está funcionando muy bien, y luego, en virtud del hecho de que alguien implementó una clase derivada totalmente válida en alguna parte completamente diferente de la aplicación, de repente tu aplicación está rota.
Y aún no he empezado a hablar de la posibilidad de que las clases base y derivada se encuentren en diferentes conjuntos. ¿Qué ocurre si hace referencia a un ensamblado con una clase base que contiene un método parcial "público" e intenta sobrescribirlo en una clase derivada en otro ensamblado? ¿El método base está allí o no? ¿Qué pasa si, originalmente, el método se implementó y escribimos un montón de código en contra, pero alguien decidió eliminar la implementación? El compilador no tiene manera de anular las llamadas a métodos parciales de las clases de referencia porque, en lo que respecta al compilador, ese método nunca existió en primer lugar. No está allí, ya no está en la IL compilada.Así que ahora, simplemente eliminando la implementación de un método parcial, que se supone que no tiene efectos negativos, hemos roto un montón de código dependiente.
Ahora algunas personas podrían estar diciendo, "así que, sé que no voy a tratar de hacer esto ilegal con métodos parciales". Lo que tiene que entender es que los métodos parciales, al igual que las clases parciales, están destinados principalmente a ayudar a simplificar la tarea de generación de código . Es muy poco probable que alguna vez quiera escribir un método parcial usted mismo período. Con el código generado por la máquina, por otro lado, es bastante probable que los consumidores del código quieran "inyectar" el código en varias ubicaciones, y los métodos parciales proporcionan una manera limpia de hacerlo.
Y ahí radica el problema. Si introduce la posibilidad de errores en tiempo de compilación debido a métodos parciales, ha creado una situación en la cual el generador de código genera código que no compila. Esta es una situación muy, muy mala. Piense en lo que haría si su herramienta de diseño favorita - por ejemplo, Linq to SQL, o el diseñador de Windows o ASP.NET, repentinamente comenzara a producir código que a veces no compila, todo porque algún otro programador creó alguna otra clase que nunca antes habías visto que se haya vuelto demasiado íntima con el método parcial?
Al final, realmente se reduce a una pregunta mucho más simple: ¿Qué métodos públicos/protegidos parciales añadir que no se puede lograr con métodos abstractos? La idea detrás de los parciales es que puedes ponerlos en clases concretas y aún así compilar. O más bien, ellos no compilarán, pero tampoco producirán un error, serán ignorados por completo. Pero si espera que se llamen públicamente, ya no son realmente "opcionales", y si espera que se anulen en una clase derivada, entonces también podría hacerlo abstracto o virtual y vacío. Realmente no hay ningún que use para un método parcial público o protegido, que no sea confundir al compilador y al pobre bastardo que intenta dar sentido a todo.
Así que, en lugar de abrir esa caja de Pandora, el equipo dijo que la olvides: los métodos parciales públicos/protegidos no son de mucha utilidad, así que hazlos privados. De esa manera podemos mantener todo seguro y cuerdo. Y es. Mientras los métodos parciales permanezcan privados, son fáciles de entender y no generan preocupaciones. ¡Mantengámoslo de esa manera!
Esto no está bien, porque los parciales se resuelven en el nivel del compilador. La asamblea no tendrá información de que el tipo fue parcial o no parcial. Entonces, si no hay implementación para el público parcial, sería razonable pensar que el compilador arrojaría un error en ese punto. –
¿No es eso más o menos lo que dije? – SLaks
Mi punto es que, dado que el compilador maneja la resolución de los parciales, sabría de antemano si existía o no una implementación. Se generaría un error en ese momento con respecto al cuerpo del método faltante, mucho antes de que se pudiera implementar cualquier código fuera de la clase que llama a ese método. –