valid - comentarios c# documentacion
¿Por qué los métodos parciales no pueden ser públicos si la implementación está en el mismo ensamblaje? (9)
Debido a que el equipo del compilador de MS no tenía un requisito para implementar esta característica.
Este es un posible escenario de lo que sucede dentro de MS, ya que VS usa gen código para muchas de sus características, un día, un desarrollador de código MS decidió que él / ella necesitaba tener métodos parciales para que la api generada por el código pudiera extenderse. forasteros, y este requisito llevó al equipo del compilador a actuar y entregar.
Según la documentación de MSDN para clases parciales:
Los métodos parciales son implícitamente privados.
Así que puedes tener esto
// Definition in file1.cs
partial void Method1();
// Implementation in file2.cs
partial void Method1()
{
// method body
}
Pero no puedes tener esto
// Definition in file1.cs
public partial void Method1();
// Implementation in file2.cs
public partial void Method1()
{
// method body
}
Pero ¿por qué es esto? ¿Hay alguna razón para que el compilador no pueda manejar métodos parciales públicos?
En lugar de preguntar por qué son privados , reformulemos la pregunta a esto:
- ¿Qué pasaría si los métodos parciales no fueran privados?
Considere este ejemplo simple:
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 pregunta de por qué haríamos de esto un método parcial en lugar de uno abstracto (volveré a eso). Lo importante es que esto compila, y funciona correctamente, ya HandleException
que se implemente o no el método HandleException
. Si nadie lo implementa, esto solo come la excepción y devuelve 0.
Ahora cambiemos las reglas, digamos 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 pequeño problema aquí. Hemos "anulado" el método HandleException
, excepto que todavía no hay ningún método para anularlo. Y quiero decir que el método literalmente no existe , no se está compilando en absoluto.
¿Qué significa lo que nuestra Calculator
base invoca HandleException
? ¿Debería invocar el método derivado (anulado)? 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 base.HandleException
? ¿Se supone que esto no hace nada? Levantar una MethodNotFoundException
? Es muy difícil seguir el principio de la menor sorpresa aquí; Casi cualquier cosa que hagas va a ser sorprendente.
O tal vez no debería pasar nada cuando se invoca HandleException
, porque no se implementó el método base. Sin embargo, esto no parece muy intuitivo. Nuestra clase derivada ha implementado este método y la clase base ha sacado la alfombra de debajo sin que nosotros lo sepamos. Puedo imaginar fácilmente a un desarrollador pobre que se arranca el pelo, incapaz de entender por qué su método anulado nunca se ejecuta.
O tal vez este código no debería compilarse en absoluto o debería producir una advertencia. Pero esto tiene una serie de problemas propios. Lo más importante es que se rompe el contrato provisto por métodos parciales, que dice que descuidar la implementación de uno nunca debería resultar en un error de compilación. Usted tiene una clase base que está zumbando muy bien, y luego, en virtud del hecho de que alguien implementó una clase derivada totalmente válida en una parte completamente diferente de la aplicación , de repente su aplicación se rompe.
Y ni siquiera he comenzado a hablar sobre la posibilidad de que las clases base y derivadas estén en diferentes ensamblajes. ¿Qué sucede si hace referencia a un conjunto con una clase base que contiene un método parcial "público" e intenta anularlo en una clase derivada en otro conjunto? ¿Está el método base allí o no? ¿Qué pasaría si, originalmente, se implementó el método y escribimos un montón de código en su contra, pero alguien decidió eliminar la implementación? El compilador no tiene forma de eliminar las llamadas de método 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 el IL del ensamblado compilado. Así que ahora, simplemente eliminando la implementación de un método parcial, que se supone que no tiene efectos nocivos, hemos eliminado un montón de código dependiente.
Ahora, algunas personas podrían estar diciendo: "y qué, sé que no voy a tratar de hacer estas cosas ilegales con métodos parciales". Lo que hay que entender es que los métodos parciales, al igual que las clases parciales, están pensados principalmente para ayudar a simplificar la tarea de generación de código . Es muy poco probable que alguna vez quieras escribir un período de método parcial. Por otro lado, con el código generado por la máquina , es bastante probable que los consumidores del código quieran "inyectar" el código en varias ubicaciones, y los métodos parciales proporcionan una forma limpia de hacerlo.
Y ahí radica el problema. Si introduce la posibilidad de errores de tiempo de compilación debido a métodos parciales, ha creado una situación en la que el generador de código genera código que no se compila . Esta es una situación muy, muy mala. Piense en lo que haría si su herramienta de diseño favorita: diga Linq to SQL, o el diseñador de Winforms o ASP.NET, de repente comenzó a producir código que a veces no se compila, todo ¿Porque algún otro programador creó alguna otra clase que nunca antes hayas visto, que se haya vuelto un poco íntima con el método parcial?
Sin embargo, al final, todo se reduce a una pregunta mucho más simple: ¿Qué agregarían los métodos parciales públicos / protegidos que ya no se puede lograr con métodos abstractos? La idea detrás de los parciales es que puedes ponerlos en clases concretas y todavía se compilarán. O más bien, no compilarán, pero tampoco producirán un error, simplemente serán ignorados por completo. Pero si espera que se los llame públicamente, entonces ya no son realmente "opcionales", y si espera que se anulen en una clase derivada, entonces también puede hacerlo abstracto o virtual y vacío. Realmente no hay ningún uso para un método parcial público o protegido, que no sea para confundir al compilador y al pobre bastardo que trata de darle sentido a todo.
Así que en lugar de abrir la caja de Pandora, el equipo dijo: olvídalo: los métodos parciales públicos / protegidos no son muy útiles de todos modos, así que hazlos privados. De esa manera podemos mantener todo sano y salvo. Y es. Mientras los métodos parciales permanezcan privados, son fáciles de entender y sin preocupaciones. ¡Mantengámoslo de esa manera!
He leído detenidamente todos sus comentarios y estoy de acuerdo con la mayoría de las conclusiones, sin embargo, tengo un escenario en el que creo que se beneficiará de los métodos parciales públicos / protegidos SIN perder la intención original:
Tengo un generador de código que genera un código de serialización y otro código de placa de caldera. En particular, genera un método de "Restablecimiento" para cada propiedad para que un diseñador como VS pueda revertir su valor al original. Dentro del código para este método, genero una llamada a un método parcial Repaint ().
La idea es que si el objeto lo desea, puede escribir el código y luego hacer algo, de lo contrario no se hace nada y el rendimiento es óptimo.
El problema es que, a veces, el método Repaint existe en el objeto para OTROS propósitos que no se llama desde el código generado, en ese momento, cuando declaro que el cuerpo del método debería poder hacerlo interno, protegido o público. Estoy definiendo el método en este punto, y sí, documentaré, etc. aquí, no en la declaración que generé sino en la que hice a mano. El hecho de que también se definió en el código generado no debería afectar esto.
Estoy de acuerdo en que la definición de modificadores de acceso en un método parcial no tiene sentido cuando se declara el método sin un cuerpo. Cuando declare el método CON un cuerpo, debe tener la libertad de declararlo de la manera que lo desee. No veo ninguna diferencia en términos de complejidad para que el compilador acepte este escenario.
Tenga en cuenta que en clases parciales, esto está perfectamente bien, puedo dejar una de las declaraciones parciales "desnuda" sin ningún modificador de acceso, pero puedo especificarlas en otra declaración parcial de la misma clase. Mientras no haya contradicciones, todo está bien.
Las razones simples son que los métodos parciales no son públicos porque son un detalle de implementación. Su objetivo principal es admitir escenarios relacionados con el diseñador y no se pretende que formen parte de una API pública compatible. Un método no público funciona bien aquí.
Permitir que los métodos parciales sean públicos es una característica. Las características tienen costos inherentes, incluidos el diseño, las pruebas, el desarrollo, etc. Los métodos parciales fueron solo una de las muchas características agregadas en un Visual Studio 2008 muy completo. Se consideraron lo más pequeños posible para ajustarse al escenario para poder salir. Espacio para más características urgentes como LINQ.
Las respuestas de Reed y Slak son totalmente correctas, pero parece que no estás dispuesto a aceptarlas.
Intentaré explicar por qué los métodos parciales se implementan con estas restricciones.
Los métodos parciales son para implementar ciertos tipos de escenarios de generación de código con eficiencia máxima, tanto en tiempo de ejecución como en sobrecarga de metadatos. La última parte es la verdadera razón de ellos, ya que están tratando de hacerlos (en palabras de Erics) "Pagar por jugar".
Cuando se agregaron métodos parciales, el JIT era completamente capaz de alinear un método vacío y, por lo tanto, no tenía ningún esfuerzo de tiempo de ejecución en los sitios de llamada. El problema es que incluso entonces hay un costo involucrado, y es que los metadatos de la clase tendrán estos métodos vacíos que aumentan su tamaño (innecesariamente) y obligan a realizar un mayor esfuerzo durante el proceso de JIT para tratar de optimizarlos.
Si bien es posible que no se preocupe demasiado por este costo (y, de hecho, muchas personas no lo notarán en absoluto) hace una gran diferencia en el código donde el costo de inicio es importante, o donde el disco / memoria está restringido. Es posible que haya notado que el uso obligatorio de .Net en Windows Mobile 7 y Zune, en estas áreas, el aumento en los metadatos de tipo es un costo considerable.
Por lo tanto, los métodos parciales se diseñan de manera tal que, si nunca se usan tienen un costo absolutamente cero, dejan de existir en la producción de cualquier manera. Esto viene con algunas restricciones importantes para garantizar que no se produzcan errores.
Tomado de la página msdn con mis notas.
- ... el método debe devolver vacío.
- Los métodos parciales pueden tener parámetros de referencia pero no de salida.
De lo contrario, eliminarles la llamada puede dejarle con un problema indefinido de con qué reemplazar la asignación.
- Los métodos parciales no pueden ser externos, porque la presencia del cuerpo determina si se están definiendo o implementando.
- Puede hacer un delegado a un método parcial que se ha definido e implementado, pero no a un método parcial que solo se ha definido.
Esto se desprende del hecho de que el compilador necesita saber si está definido o no, y por lo tanto es seguro para su eliminación. Esto nos lleva a la que no te gusta.
- Los métodos parciales son implícitamente privados, y por lo tanto no pueden ser virtuales.
Su modelo de metal de esta característica es que, si el compilador sabe que se implementa un método parcial, simplemente debe permitir que el método parcial sea público (y virtual para esa materia) ya que puede verificar que usted implementó el método.
Si cambiara la función para hacer esto, tiene dos opciones:
Forzar todos los métodos parciales no privados para requerir implementación.
- simple y no es probable que implique mucho esfuerzo, pero entonces cualquiera de estos métodos ya no es parcial en el sentido significativo del plan original.
Cualquier método declarado público simplemente se elimina si no se implementó en el ensamblaje.
- Esto permite aplicar la eliminación parcial.
- Requiere mucho más esfuerzo por parte del compilador (para detectar todas las referencias al método en lugar de simplemente tener que buscar en la propia clase compuesta)
- La implementación de IntelliSense es un poco confusa, ¿debería mostrarse el método? ¿Se siembra solo cuando se le ha dado una definición?
- La resolución de sobrecarga se vuelve mucho más compleja, ya que debe decidir si una llamada a un método de este tipo sin definición es a) una falla en el tiempo de compilación o b) resulta en la selección de la mejor opción, si está disponible.
- Los efectos secundarios dentro de las expresiones en los sitios de llamadas ya son complejos en el caso privado. Esto se ve mitigado en parte por el supuesto de que las implementaciones parciales ya muestran un alto grado de acoplamiento. Este cambio aumentaría el potencial de acoplamiento.
- Esto tiene modos de falla bastante complejos en cuanto a que los métodos públicos podrían ser eliminados silenciosamente por algún cambio inocuo en el ensamblaje original. Otros ensamblajes que dependan de este fallarían (en tiempo de compilación) pero con un error muy confuso (sin un esfuerzo considerable, esto se aplicaría incluso a proyectos en la misma solución).
La solución 1 simplemente no tiene sentido, agrega esfuerzo y no beneficia a los objetivos originales. Peor aún, en realidad puede obstaculizar los objetivos originales, ya que una persona que usa métodos parciales de esta manera puede no darse cuenta de que no está obteniendo ninguna reducción en los metadatos. Además, alguien puede confundirse acerca de los errores que resultan de no proporcionar la definición.
Eso nos deja con la solución 2. Esta solución implica un esfuerzo (y cada característica comienza con -100 ), por lo que debe agregar algún beneficio convincente para obtenerla sobre el -100 (y los negativos adicionales agregados para la confusión causada por no los casos adicionales) ). ¿Qué escenarios se te ocurren para obtener cosas positivas?
Su ejemplo motivador en los comentarios anteriores fue "tener comentarios y / o atributos en un archivo diferente"
La motivación para los comentarios XML fue completamente aumentar la ubicación de la documentación y el código. Si su verbosidad es alta, entonces existe un esquema para mitigar esto. Mi opinión es que esto no es suficiente para merecer la característica.
La capacidad de enviar atributos a una ubicación separada no me parece tan útil, de hecho, creo que tener que buscar en dos ubicaciones es más confuso. La implementación privada actual solo tiene este problema también, pero es inevitable y, de nuevo, se ve mitigada en parte por el supuesto de que un acoplamiento alto que no es visible fuera de la clase no es tan malo como el acoplamiento externo externo a la clase.
Si puede demostrar alguna otra razón convincente, estoy seguro de que sería interesante, pero los aspectos negativos a superar son considerables.
Los métodos parciales tienen que ser completamente solucionables en tiempo de compilación. Si no están allí en tiempo de compilación, faltan completamente en la salida. La razón por la que funcionan los métodos parciales es que eliminarlos no tiene ningún impacto en la API o el flujo del programa fuera del sitio de llamada de una sola línea (lo cual, también, es la razón por la que tienen que devolver el vacío).
Cuando agrega un método a su API pública, está definiendo un contrato para otros objetos. Dado que todo el objetivo de un método parcial es hacerlo opcional, básicamente estaría diciendo: "Tengo un contrato en el que puede confiar. Oh, espera, no puede confiar en este método".
Para que la API pública sea razonable, el método parcial tiene que estar siempre allí o desaparecer, en cuyo caso no debería ser parcial.
En teoría, los diseñadores de lenguaje podrían haber cambiado la forma en que funcionan los métodos parciales, para permitir esto. En lugar de eliminarlos de donde se les llamaba, podrían haberlos implementado utilizando un código auxiliar (es decir, un método que no hace nada). Sin embargo, esto no sería tan eficiente y no es necesario para el caso de uso previsto por métodos parciales.
No estoy seguro de si esto responderá a tu pregunta, sí respondió a la mía. Métodos parciales
Extractos:
Si se le permitiera devolver algo y, a su vez, utilizaba eso como un valor de retorno de su función CallMyMethods, tendría problemas cuando no se implementaran los métodos parciales.
Si no define el método, ¿qué debe hacer el compilador?
Si no los define, los métodos parciales no serán compilados en absoluto .
Esto solo es posible porque el compilador sabe dónde están todas las llamadas al método. Si el método no está definido, eliminará completamente las líneas de código que llaman al método. (Esta es también la razón por la que solo pueden devolver void
)
Si el método es público, esto no puede funcionar, ya que el método puede ser llamado por un ensamblado diferente, sobre el cual el compilador no tiene control.
partial
métodos partial
serán eliminados por el compilador si no tiene una implementación. Según mi entendimiento, el compilador no podrá eliminar los métodos partial
si los métodos son accesibles como public
//partial class with public modifier
public partial class Sample
{
partial void Display();
}
public partial class Sample
{
partial void Display() { }
}
Podremos acceder a métodos public
como los siguientes, lo que constituirá una restricción para que el compilador elimine el método partial
que no está implementado.
// referred partial method which doesn''t have implementation
var sample = new Sample();
sample.Display();