2010-01-08 16 views
15

Según MSDN Documentation para las clases parciales:¿Por qué los métodos parciales no son públicos si la implementación está en el mismo conjunto?

métodos parciales son implícitamente privada

para que pueda tener este

// Definition in file1.cs 
partial void Method1(); 

// Implementation in file2.cs 
partial void Method1() 
{ 
    // method body 
} 

pero no se puede tener este

// 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 por la cual el compilador no puede manejar los métodos parciales públicos?

Respuesta

5

Porque el equipo del compilador de MS no tenía un requisito para implementar esta característica.

Aquí hay un posible escenario de lo que sucede dentro de MS, porque VS usa gen de código para muchas de sus características, un día un desarrollador genérico de código MS decidió que necesitaba métodos parciales para que el código generase una API podría ser extendido por personas externas, y este requisito llevó al equipo del compilador a actuar y cumplir.

0

No estoy seguro si esto responderá a su pregunta, sí contestó la mía. Partial Methods

Extractos:

Si se permite que devolver algo, y fueron a su vez usando eso como un valor regreso de sus CallMyMethods función, se podría tener problemas cuando los métodos parciales no fueron implementados .

3

Si no define el método, ¿qué debería hacer el compilador?

Si no los define, los métodos parciales no se compilarán en total.
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, porque el método podría ser llamado por un ensamblaje diferente, sobre el cual el compilador no tiene control.

+1

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

+1

¿No es eso más o menos lo que dije? – SLaks

+1

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

27

Los métodos parciales deben resolverse completamente en tiempo de compilación. Si no están allí en tiempo de compilación, faltan por completo en la salida. La razón por la cual funcionan los métodos parciales es que eliminarlos no tiene impacto en la API o el flujo de programas fuera del sitio de una línea (que, además, es el motivo por el 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 el objetivo de un método parcial es hacerlo opcional, básicamente estarías diciendo: "Tengo un contrato en el que puedes confiar. Oh, espera, no puedes confiar en este método".

Para que la API pública sea razonable, el método parcial tiene que estar siempre presente o siempre desaparecido, en cuyo caso no debe ser parcial.

En teoría, los diseñadores del lenguaje podrían haber cambiado la forma en que funcionan los métodos parciales, para permitir esto. En lugar de eliminarlos de todos los lugares donde fueron llamados, podrían haberlos implementado usando un stub (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.

+6

Correcto.Hacerlo sería totalmente contrario al propósito de los métodos parciales, a saber, que son "pagar por jugar" en la carga de metadatos. Puedes tener mil métodos parciales, y si defines solo uno de ellos, obtienes metadatos emitidos para ese único método, no para miles de ellos. Diseñamos métodos parciales para escenarios de codificación generados por máquina en los que literalmente podría haber miles de ellos, y no queremos inflar los metadatos. –

+0

Sí, eso era lo que estaba tratando de decir, tú podrías haberlo diseñado de esa manera, pero habría cambiado su significado dramáticamente. –

+0

Reed No creo que haya sido lo suficientemente claro con mi pregunta. mi escenario está teniendo tanto la implementación como la definición en la misma asamblea. He actualizado el título para reflejar esto. – Simon

8

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!

+0

Pero mi ejemplo no tiene herencia. Simplemente quiero tener comentarios y/o atributos en un archivo separado para la implementación – Simon

+3

@Simon: ¿Por qué importaría si "su ejemplo" no hace uso de una construcción en particular? Estoy explicando por qué la función no se puede implementar de forma segura. Usted no es el único usuario del compilador de C#. Además, como expliqué anteriormente, los métodos parciales están diseñados para su uso en la generación de código; su caso de uso probablemente ni siquiera está en el radar. – Aaronaught

+0

@Simon: Y lo que es más importante, su caso de uso es aún más peligroso que el anterior. ¿Qué pasa si alguien, en cualquier lugar, puede agregar los atributos '[Conditional]' or '[MethodImpl]'? Esto significa que otras personas podrían introducir cambios importantes y potencialmente disruptivos en su clase en el momento de la compilación. Una clase necesita ser capaz de mantener sus propias invariantes; Permitir la extensión arbitraria de los métodos públicos de esta manera lo elimina por completo. Incluso la capacidad de un actor externo para cambiar la documentación parece artificial; ¿Por qué querrías permitir esto? – Aaronaught

2

Las respuestas de Reed y Slak son enteramente correctas, pero no parece dispuesto a aceptar sus respuestas.

Trataré de explicar por qué los métodos parciales se implementan con estas restricciones.

Los métodos parciales son para implementar ciertos tipos de escenarios genéricos de código con máxima eficiencia, tanto en tiempo de ejecución como en sobrecarga de metadatos. La última parte es la verdadera razón para ellos, ya que están tratando de hacerlos (en palabras de Erics) "Pagar por jugar".

Cuando se agregaron métodos parciales, el JIT fue completamente capaz de delimitar un método vacío y, por lo tanto, tener cero esfuerzo de ejecución en los sitios de llamadas. El problema es que, aun así, hay un costo que implica que los metadatos de la clase tendrán estos métodos vacíos aumentando su tamaño (innecesariamente) y forzando un mayor esfuerzo durante el proceso JITing 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) hace una gran diferencia en el código donde importa el costo de inicio, o donde el disco/memoria está restringido.Es posible que haya notado el uso ahora obligatorio de .Net en Windows Mobile 7 y Zune, en estas áreas, los metadatos de tipo inflado son un costo considerable.

Por lo tanto, los métodos parciales están diseñados de forma que, si nunca se utilizan, tienen un costo absolutamente nulo, dejan de existir en la salida de ninguna manera. Esto viene con algunas limitaciones importantes para garantizar que esto no dé lugar a errores.

Tomado de msdn page con mis notas.

  • ... el método debe volverse vacío.
  • Los métodos parciales pueden tener parámetros de referencia pero no de salida.

De lo contrario, quitar la llamada puede dejarle con un problema indefinido de con qué reemplazar la tarea.

  • Los métodos parciales no pueden ser externos, porque la presencia del cuerpo determina si están definiendo o implementando.
  • Puede delegar en un método parcial que se ha definido e implementado, pero no en un método parcial que solo se ha definido.

Estas se derivan 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 el caso) ya que puede verificar que implementó el método.

¿Se le para cambiar la función de hacer esto, tiene dos opciones:

  1. forzar a todos los métodos parciales privados no requieren a aplicación.

    • simple y no es probable que impliquen mucho esfuerzo, pero cualquiera de estos métodos ya no son parciales en el sentido significativo del plan original.
  2. Cualquier método declarado público se elimina simplemente si no se implementó en el ensamblado.

    • Esto permite que la eliminación parcial que se aplicará
    • Se requiere mucho más esfuerzo por el compilador (para detectar todas las referencias al método en lugar de simplemente necesidad de mirar 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 dicho método sin definición es a) un error de compilación ob) da como resultado la selección de la siguiente mejor opción, si está disponible.
    • Los efectos secundarios dentro de las expresiones en los sitios de llamada ya son complejos en el caso privado. Esto se mitiga en cierta medida suponiendo 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 el sentido de que los métodos públicos podrían eliminarse silenciosamente mediante algún cambio inocuo en el ensamblaje original. Otros ensambles que dependen 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).

Solución 1 es simplemente sentido, se añade el esfuerzo y no es beneficiosa para los objetivos originales. Peor aún, puede obstaculizar los objetivos originales, ya que alguien que utiliza métodos parciales de esta manera puede no darse cuenta de que no obtienen ninguna reducción en los metadatos. También 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 esfuerzo (y every feature starts with -100) por lo que debe agregar algún beneficio convincente para obtenerlo por encima de -100 (y los negativos adicionales añadidos por la confusión causada por los casos de borde no adicionales) . ¿Qué escenarios se te pueden ocurrir para que todo sea positivo?

Su ejemplo motivador en los comentarios anteriores era "tener comentarios y/o atributos en un archivo diferente"

La motivación de los comentarios XML era del todo para aumentar la localidad de documentación y código. Si su nivel de detalle es alto, entonces existe un esquema para mitigar esto. Mi opinión es que esto no es suficiente para merecer la función.

La capacidad de insertar atributos en una ubicación diferente no me parece útil, de hecho, creo que tener que buscar en dos ubicaciones es más confuso. La única implementación privada actual también tiene este problema, pero es inevitable y, de nuevo, se mitiga en cierto modo suponiendo que el alto acoplamiento que no es visible fuera de la clase no es tan malo como el alto acoplamiento 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.

0

La razón simple es que los métodos parciales no son públicos porque son un detalle de implementación. Están destinados principalmente a admitir escenarios relacionados con el diseñador y no están destinados a ser 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, incluido el diseño, las pruebas, el desarrollo, etc. Los métodos parciales fueron solo una de las muchas funciones agregadas en un Visual Studio 2008. El alcance fue lo más pequeño posible para ajustarse al escenario y salir espacio para funciones más urgentes, como LINQ.

+0

¿Puede detallar más por qué alguna vez querría tener un método parcial? Mi mente == sopló que incluso existen en primer lugar ... –

+0

@Paul: Son increíblemente útiles para situaciones generadas por diseñadores o plantillas. Es útil tener métodos "opcionales" que el diseñador puede completar y luego permitirle usarlos si es necesario. Mucho menos útil para el código escrito a mano. –

2

He leído a fondo todos sus comentarios y estoy de acuerdo con la mayoría de las conclusiones, sin embargo tengo un escenario en el que creo que va a beneficiarse de los métodos parciales públicas/protegido sin perder la intención original:

tengo un generador de código que genera 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 de este método genero una llamada a un método parcial Repintado().

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, algunas veces, el método Repaint existe en el objeto para otros propósitos que no sean llamados desde el código generado, en ese punto, cuando declaro el método cuerpo podría 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.

Acepto 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 ser libre de declararlo de la manera que lo desee. No veo ninguna diferencia en términos de complejidad para el compilador para aceptar este escenario.

Observe que en clases parciales, esto está perfectamente bien, puedo dejar una de las declaraciones parciales "desnudas" 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.

0

partial Los métodos serán eliminados por el compilador si no tiene una implementación. Según mi entendimiento compilador no será capaz de 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 public métodos como el de abajo que será una restricción para el compilador para quitar el método partial eso no está implementado.

// referred partial method which doesn't have implementation 
var sample = new Sample(); 
sample.Display(); 
Cuestiones relacionadas