2009-02-23 10 views
27

Estoy desarrollando un conjunto de clases que implementan una interfaz común . Un consumidor de mi biblioteca debe esperar que cada una de estas clases implemente un cierto conjunto de funciones estáticas. ¿Hay alguna forma de que pueda decorar estas clases para que el compilador capte el caso donde una de las funciones no se implementa?¿Hay alguna manera de forzar una clase C# para implementar ciertas funciones estáticas?

Sé que eventualmente se detectará al generar el código de consumo. Y también sé cómo evitar este problema usando una clase de fábrica.

Solo curiosidad por saber si hay alguna sintaxis/atributos para requerir funciones estáticas en una clase.

Ed Se ha eliminado la palabra 'interfaz' para evitar confusiones.

+0

Perdón por mi curiosidad, pero no puedo evitar preguntarme ¿por qué necesitarías una función como esta? ¿Qué métodos estáticos tienen los métodos de instancias ordinarias (no estáticos) que no tienen? Nada viene a mi mente. ¿Quieres compartir un contexto para tu pregunta? – Dan

+2

No necesita una instancia para llamar a un método estático. Entonces son agradables de usar como métodos de fábrica, por ejemplo, un método de deserialización. – tpower

+0

@Dan: un uso adicional sería la capacidad de usar la interfaz sin saber si está trabajando en una instancia o un tipo (o cambiar eso más adelante). Piense en una clase estática que represente algún tipo de lista singleton, y que debería, como una lista adecuada, implementar 'IList '. Claro, la solución alternativa de almacenar una instancia de una clase que implemente 'IList ' en la clase estática es factible (y soy consciente de que C# actualmente por ahora solo lo permite), pero la capacidad de declarar directamente una clase como estática la implementación de una interfaz eliminaría un nivel innecesario de indirección. –

Respuesta

28

No, no hay soporte de idioma para esto en C#. Hay dos soluciones que se me ocurren de inmediato:

  • use reflection at runtime; dedos cruzados y la esperanza ...
  • utilizan un conjunto unitario/default-instancia/similar a implementar una interfaz que declara los métodos

(actualización)

En realidad, siempre y cuando tenga la unidad -prueba, la primera opción no es tan mala como se podría pensar si (como yo) vienes de un estricto historial de "escritura estática". El hecho es; funciona bien en lenguajes dinámicos. Y de hecho, así es exactamente como funciona mi código generic operators - espera tiene los operadores estáticos. En tiempo de ejecución, si no lo hace, se reirá de ti con un tono de burla adecuado ... pero no puede verificarlo en tiempo de compilación.

+11

Me encantaría que el código se riera de mí en un tono de burla adecuado. Espera, eso pasaría todo el tiempo. No importa. ;-) –

4

Lamentablemente, no, no hay nada como esto integrado en el idioma.

0

No, no tendría sentido esta función. Las interfaces son básicamente una forma reducida de herencia múltiple. Le dicen al compilador cómo configurar la tabla de funciones virtuales para que los métodos virtuales no estáticos se puedan llamar correctamente en las clases descendientes. Los métodos estáticos no pueden ser virtuales, por lo tanto, no tiene sentido usar interfaces para ellos.

+2

Definitivamente * es * un punto en esto: solo porque los métodos no se llamarán a través de la interfaz no significa que no valga la pena probar que están allí. Es un poco como la nueva restricción T() que C# expone - la implementación real de la nueva T() efectivamente pasa a través de la reflexión ... –

+0

No quiero que sean virtuales, solo oblígales a existir. – tpower

+0

Estoy con dsimcha en esto. Si el propósito de la interfaz explícitamente declarada no es separar la interfaz de la implementación, entonces no queda mucha "interfaz". Si necesita que algunas clases tengan algunos métodos, entonces podría inventar algún otro elemento de languge, como HasToImplementMethodsAttrib – Dan

15

Básicamente, parece que buscas una especie de "polimorfismo estático". Eso no existe en C#, aunque he sugerido un tipo de "static interface" notion which could be useful in terms of generics.

Una cosa que podría hacer es escribir una prueba de unidad simple para verificar que todos los tipos en un conjunto particular obedezcan sus reglas. Si otros desarrolladores también implementarán la interfaz, puede poner ese código de prueba en un lugar común para que todos los que implementen la interfaz puedan probar fácilmente sus propios ensamblajes.

+0

Podría especificarse en un contrato de interfaz que todas las implementaciones * legítimas * de la interfaz deben tener ciertos miembros estáticos * y *, si no están selladas, incluyen en su contrato de herencia un requisito de que todos los tipos derivados * legítimos deben cumplir con los mismos requisitos. Eso no evitaría que las personas escriban implementaciones ilegítimas de la interfaz, pero la situación no sería peor que con casi cualquier otro tipo de interfaz. – supercat

+0

@supercat: ¿Por "contrato" solo quiere decir "documentación" o algo así como contratos de código? –

+0

Quise decir documentación. Cualquier interfaz significativa debe tener un conjunto de requisitos, tales que las implementaciones que cumplan esos requisitos sean legítimas, y las implementaciones que no cumplan esos requisitos sean ilegítimas. No hay un formato fijo en el que se expresen dichos requisitos, pero si, por ejemplo, una implementación de 'IList ' tenía una propiedad setter que ignoraba el parámetro 'index' y simplemente sobrescribía un elemento aleatorio, muchos métodos que esperan trabajar con un' IList 'fallarían, no porque los métodos fueran defectuosos, sino porque el La implementación de 'IList ' era ilegítima. – supercat

3

Aunque no hay soporte de idioma para esto, puede usar una herramienta de análisis estático para aplicarlo. Por ejemplo, podría escribir una regla personalizada para FxCop que detecte la implementación de un atributo o interfaz en una clase y luego verifique la existencia de ciertos métodos estáticos.

0

El enfoque que lo acerca más a lo que necesita es un singleton, como sugirió Marc Gravell.

Las interfaces, entre otras cosas, le permiten proporcionar un cierto nivel de abstracción a sus clases para que pueda usar una API dada, independientemente del tipo que la implemente. Sin embargo, dado que SI necesita saber el tipo de una clase estática para usarla, ¿por qué querría aplicar esa clase para implementar un conjunto de funciones?

Tal vez podría utilizar un atributo personalizado como [ImplementsXXXInterface] y proporcionar alguna comprobación en tiempo de ejecución para asegurarse de que las clases con este atributo realmente implementen la interfaz que necesita.

+0

La comprobación del tiempo de ejecución no es buena porque el compilador ya la recogerá cuando construya el código del consumidor. Me preguntaba si podría hacer que el compilador lo recoja antes, eso es todo. – tpower

1

El patrón singleton no ayuda en todos los casos. Mi ejemplo es de un proyecto real mío. No es artificial.

Tengo una clase (llamémosla "Widget") que hereda de una clase en un ORM de un tercero. Si instancia un objeto Widget (por lo tanto, creo una fila en el db) solo para asegurarme de que mis métodos estáticos están declarados, estoy haciendo un desastre mayor que el que estoy tratando de limpiar.

Si creo este objeto extra en el almacén de datos, tengo que ocultarlo de los usuarios, cálculos, etc.

uso de interfaces en C# para asegurarse de que implemento características comunes en un conjunto de clases

Algunos de los métodos que implementan estas características requieren datos de instancia para ejecutarse. Codigo estos métodos como métodos de instancia y uso una interfaz C# para asegurarme de que existan en la clase.

Algunos de estos métodos no requieren datos de instancia, por lo que son métodos estáticos. Si pudiera declarar interfaces con métodos estáticos, el compilador podría verificar si estos métodos existen o no en la clase que dice que implementa la interfaz.

5

Esta es una gran pregunta y una que he encontrado en mis proyectos.

Algunas personas sostienen que las interfaces y las clases abstractas existen solo para el polimorfismo, no para obligar a los tipos a implementar ciertos métodos. Personalmente, considero que el polimorfismo es un caso de uso primario, y la implementación forzada es secundaria. Utilizo la técnica de implementación forzada con bastante frecuencia. Por lo general, aparece en el código de marco que implementa un patrón de plantilla. La clase base/template encapsula algunas ideas complejas, y las subclases proporcionan numerosas variaciones al implementar los métodos abstractos. Un beneficio pragmático es que los métodos abstractos proporcionan orientación a otros desarrolladores que implementan las subclases. Visual Studio incluso tiene la capacidad de stub los métodos para usted. Esto es especialmente útil cuando un desarrollador de mantenimiento necesita agregar una nueva subclase meses o años después.

El inconveniente es que no hay soporte específico para algunos de estos escenarios de plantilla en C#. Los métodos estáticos son uno. Otro es constructores; idealmente, ISerializable debería obligar al desarrollador a implementar el constructor de serialización protegido.

El enfoque más sencillo probablemente sea (como se sugirió anteriormente) utilizar una prueba automática para verificar que el método estático se implemente en los tipos deseados. Otra idea viable ya mencionada es implementar una regla de análisis estático.

Una tercera opción es utilizar un marco de Programación Orientada a Aspectos como PostSharp. PostSharp admite la validación de aspectos en tiempo de compilación. Puede escribir código .NET que refleje sobre el conjunto en tiempo de compilación, generando advertencias y errores arbitrarios. Por lo general, haces esto para validar que el uso de un aspecto es apropiado, pero no veo por qué no podrías usarlo también para validar reglas de plantilla.

0

Si sólo después de conseguir esos errores de compilación, tenga en cuenta esta configuración:

  1. definir los métodos en una interfaz.
  2. Declare los métodos con abstract.
  3. Implemente los métodos públicos estáticos, y haga que el método abstracto anule simplemente llame a los métodos estáticos.

Es un poco de código adicional, pero sabrá cuando alguien no esté implementando un método requerido.

Cuestiones relacionadas