¿Por qué se diseñó C # de esta manera?
Según tengo entendido, una interfaz solo describe el comportamiento y tiene el propósito de describir una obligación contractual para las clases que implementan la interfaz de que se implementa cierto comportamiento.
Si las clases desean implementar ese comportamiento en un método compartido, ¿por qué no deberían hacerlo?
Aquí hay un ejemplo de lo que tengo en mente:
// These items will be displayed in a list on the screen.
public interface IListItem {
string ScreenName();
...
}
public class Animal: IListItem {
// All animals will be called "Animal".
public static string ScreenName() {
return "Animal";
}
....
}
public class Person: IListItem {
private string name;
// All persons will be called by their individual names.
public string ScreenName() {
return name;
}
....
}
c#
oop
language-features
Kramii
fuente
fuente
IListItem.ScreenName() => ScreenName()
(usando la sintaxis C # 7) implementará el método de interfaz explícitamente llamando al método estático. Sin embargo, las cosas se ponen feas cuando agregas herencia a eso (tienes que volver a implementar la interfaz)Respuestas:
Asumiendo que estás preguntando por qué no puedes hacer esto:
Esto no tiene sentido para mí, semánticamente. Los métodos especificados en una interfaz deben estar allí para especificar el contrato para interactuar con un objeto. Los métodos estáticos no le permiten interactuar con un objeto: si se encuentra en la posición en la que su implementación podría hacerse estática, es posible que deba preguntarse si ese método realmente pertenece a la interfaz.
Para implementar su ejemplo, le daría a Animal una propiedad const, que aún le permitiría acceder desde un contexto estático, y devolvería ese valor en la implementación.
Para una situación más complicada, siempre puede declarar otro método estático y delegarlo. Al tratar de encontrar un ejemplo, no puedo pensar en ninguna razón por la que harías algo no trivial en un contexto tanto estático como de instancia, así que te ahorraré un blob FooBar y lo tomaré como una indicación de que podría No es una buena idea.
fuente
public static object MethodName(this IBaseClass base)
dentro de una clase estática. Sin embargo, la desventaja es que, a diferencia de una herencia de interfaz, esto no obliga / permite que los herederos individuales anulen bien la metodología.Mi razón técnica (simplificada) es que los métodos estáticos no están en la vtable , y el sitio de la llamada se elige en tiempo de compilación. Es la misma razón por la que no puede tener anulación o miembros estáticos virtuales. Para obtener más detalles, necesitaría un graduado de CS o un compilador wonk, de los cuales yo tampoco.
Por razones políticas, citaré a Eric Lippert (que es un compilador wonk, y tiene una Licenciatura en Matemáticas, Informática y Matemáticas Aplicadas de la Universidad de Waterloo (fuente: LinkedIn ):
Tenga en cuenta que Lippert deja espacio para el llamado método de tipo:
pero aún no se ha convencido de su utilidad.
fuente
object
sy cómo se les permite implementar interfaces.La mayoría de las respuestas aquí parecen perder todo el punto. El polimorfismo puede usarse no solo entre instancias, sino también entre tipos. Esto a menudo es necesario cuando usamos genéricos.
Supongamos que tenemos un parámetro de tipo en un método genérico y necesitamos hacer alguna operación con él. No queremos crear instancias, porque desconocemos a los constructores.
Por ejemplo:
Desafortunadamente, solo puedo encontrar alternativas "feas":
Usa la reflexión fea y supera la idea de interfaces y polimorfismo.
Crear clase de fábrica completamente separada
Esto podría aumentar considerablemente la complejidad del código. Por ejemplo, si estamos tratando de modelar objetos de dominio, cada objeto necesitaría otra clase de repositorio.
Instanciar y luego llamar al método de interfaz deseado
Esto puede ser difícil de implementar incluso si controlamos el origen de las clases, que se utilizan como parámetros genéricos. La razón es que, por ejemplo, podríamos necesitar que las instancias estén solo en un estado bien conocido, "conectado a la base de datos".
Ejemplo:
Para utilizar la creación de instancias para resolver el problema de la interfaz estática, debemos hacer lo siguiente:
Esto es obviamente feo y también innecesario complica el código para todos los demás métodos. ¡Obviamente, tampoco es una solución elegante!
fuente
public abstract class DBObject<T> where T : DBObject<T>, new()
y luego hacer que todas las clases de DB hereden comoDBObject<T>
. Luego puede hacer que la recuperación por clave sea una función estática con el tipo de retorno T en la superclase abstracta, hacer que esa función cree un nuevo objeto de T y luego llamar a un objeto protegidoString GetRetrieveByKeyQuery()
(definido como abstracto en la superclase) en ese objeto para obtener La consulta real a ejecutar. Aunque esto podría estar recibiendo un poco fuera de temaSé que es una vieja pregunta, pero es interesante. El ejemplo no es el mejor. Creo que sería mucho más claro si mostraras un caso de uso:
El simple hecho de que los métodos estáticos implementen una interfaz no lograría lo que desea; lo que se necesitaría sería tener miembros estáticos como parte de una interfaz. Ciertamente, puedo imaginar muchos casos de uso para eso, especialmente cuando se trata de poder crear cosas. Dos enfoques que podría ofrecer que podrían ser útiles:
Ninguno de estos enfoques es realmente atractivo. Por otro lado, esperaría que si existieran los mecanismos en CLR para proporcionar este tipo de funcionalidad limpiamente, .net permitiría especificar restricciones "nuevas" parametrizadas (ya que saber si una clase tiene un constructor con una firma particular parecería ser comparable en dificultad a saber si tiene un método estático con una firma particular).
fuente
Miopía, supongo.
Cuando se diseñaron originalmente, las interfaces solo se utilizaron con instancias de clase
Fue solo con la introducción de interfaces como restricciones para los genéricos que agregar un método estático a una interfaz tuvo un uso práctico.
(respondiendo al comentario :) Creo que cambiarlo ahora requeriría un cambio en el CLR, lo que conduciría a incompatibilidades con los ensamblajes existentes.
fuente
Las interfaces especifican el comportamiento de un objeto.
Los métodos estáticos no especifican el comportamiento de un objeto, sino el comportamiento que afecta a un objeto de alguna manera.
fuente
En la medida en que las interfaces representan "contratos", parece razonablemente razonable que las clases estáticas implementen interfaces.
Todos los argumentos anteriores parecen perder este punto sobre los contratos.
fuente
Debido a que el propósito de una interfaz es permitir el polimorfismo, poder pasar una instancia de cualquier número de clases definidas que se hayan definido para implementar la interfaz definida ... garantizando que dentro de su llamada polimórfica, el código podrá encontrar El método que estás llamando. no tiene sentido permitir un método estático para implementar la interfaz,
¿Cómo lo llamarías?
fuente
Whatever<T>() where T:IMyStaticInterface
, podría llamarWhatever<MyClass>()
y tenerT.MyStaticMethod()
dentro de laWhatever<T>()
implementación sin necesidad de una instancia. El método para llamar se determinaría en tiempo de ejecución. Puede hacer esto a través de la reflexión, pero no hay ningún "contrato" que forzar.Con respecto a los métodos estáticos utilizados en contextos no genéricos, estoy de acuerdo en que no tiene mucho sentido permitirlos en las interfaces, ya que de todos modos no podría llamarlos si tuviera una referencia a la interfaz. Sin embargo, existe un agujero fundamental en el diseño del lenguaje creado mediante el uso de interfaces NO en un contexto polimórfico, sino genérico. En este caso, la interfaz no es una interfaz sino una restricción. Debido a que C # no tiene el concepto de una restricción fuera de una interfaz, le falta una funcionalidad sustancial. Caso en punto:
Aquí no hay polimorfismo, el genérico usa el tipo real del objeto y llama al operador + =, pero esto falla ya que no puede decir con certeza que ese operador existe. La solución simple es especificarlo en la restricción; la solución simple es imposible porque los operadores son estáticos y los métodos estáticos no pueden estar en una interfaz y (aquí está el problema) las restricciones se representan como interfaces.
Lo que C # necesita es un tipo de restricción real, todas las interfaces también serían restricciones, pero no todas las restricciones serían interfaces, entonces podría hacer esto:
Ya se ha hablado mucho sobre cómo hacer un IArithmetic para todos los tipos numéricos para implementar, pero existe una preocupación sobre la eficiencia, ya que una restricción no es una construcción polimórfica, hacer una restricción CArithmetic resolvería ese problema.
fuente
T
debería tener una estática miembro (por ejemplo, una fábrica que produceT
instancias). Incluso sin cambios en el tiempo de ejecución, creo que sería posible definir convenciones y métodos auxiliares que permitirían a los idiomas implementar algo así como el azúcar sintáctico de una manera eficiente e interoperable. Si para cada interfaz con métodos estáticos virtuales hubiera una clase auxiliar ...Porque las interfaces están en la estructura de herencia, y los métodos estáticos no heredan bien.
fuente
Lo que parece querer permitiría que se llamara a un método estático a través del Tipo o cualquier instancia de ese tipo. Esto al menos resultaría en una ambigüedad que no es un rasgo deseable.
Habría un sinfín de debates sobre si importaba, cuál es la mejor práctica y si hay problemas de rendimiento al hacerlo de una forma u otra. Simplemente no admitiéndolo, C # nos ahorra tener que preocuparnos por eso.
También es probable que un compilador que se ajustara a este deseo perdería algunas optimizaciones que pueden venir con una separación más estricta entre la instancia y los métodos estáticos.
fuente
Puede pensar que los métodos estáticos y los métodos no estáticos de una clase son interfaces diferentes. Cuando se llama, los métodos estáticos se resuelven en el objeto de clase estática singleton, y los métodos no estáticos se resuelven en la instancia de la clase con la que se trata. Entonces, si usa métodos estáticos y no estáticos en una interfaz, efectivamente estaría declarando dos interfaces cuando realmente queremos que se usen interfaces para acceder a una cosa cohesiva.
fuente
Para dar un ejemplo donde me falta la implementación estática de los métodos de interfaz o lo que Mark Brackett introdujo como el "llamado método de tipo":
Al leer desde el almacenamiento de una base de datos, tenemos una clase genérica de DataTable que maneja la lectura desde una tabla de cualquier estructura. Toda la información específica de la tabla se coloca en una clase por tabla que también contiene datos para una fila de la base de datos y que debe implementar una interfaz IDataRow. En IDataRow se incluye una descripción de la estructura de la tabla para leer de la base de datos. DataTable debe solicitar la estructura de datos de IDataRow antes de leer desde el DB. Actualmente esto se parece a:
GetDataStructure solo se requiere una vez para cada tabla para leer, la sobrecarga para crear instancias de una instancia más es mínima. Sin embargo, sería bueno en este caso aquí.
fuente
FYI: Podría obtener un comportamiento similar al que desea al crear métodos de extensión para la interfaz. El método de extensión sería un comportamiento estático compartido, no reemplazable. Sin embargo, desafortunadamente, este método estático no formaría parte del contrato.
fuente
Las interfaces son conjuntos abstractos de funcionalidad disponible definida.
Si un método en esa interfaz se comporta como estático o no es un detalle de implementación que debe ocultarse detrás de la interfaz . Sería incorrecto definir un método de interfaz como estático porque forzaría innecesariamente el método a implementarse de cierta manera.
Si los métodos se definieran como estáticos, la clase que implementa la interfaz no estaría tan encapsulada como podría ser. La encapsulación es algo bueno en el diseño orientado a objetos (no voy a explicar por qué, puede leer eso aquí: http://en.wikipedia.org/wiki/Object-oriented ). Por esta razón, los métodos estáticos no están permitidos en las interfaces.
fuente
Las clases estáticas deberían poder hacer esto para que puedan usarse genéricamente. En su lugar, tuve que implementar un Singleton para lograr los resultados deseados.
Tuve un montón de clases Static Business Layer que implementaron métodos CRUD como "Crear", "Leer", "Actualizar", "Eliminar" para cada tipo de entidad como "Usuario", "Equipo", etc. .. Luego creé una base control que tenía una propiedad abstracta para la clase Business Layer que implementaba los métodos CRUD. Esto me permitió automatizar las operaciones "Crear", "Leer", "Actualizar", "Eliminar" de la clase base. Tuve que usar un Singleton debido a la limitación estática.
fuente
La mayoría de las personas parecen olvidar que en OOP las clases también son objetos, por lo que tienen mensajes, que por alguna razón c # llama "método estático". El hecho de que existan diferencias entre los objetos de instancia y los objetos de clase solo muestra fallas o deficiencias en el lenguaje. Aunque optimista sobre c # ...
fuente
OK, aquí hay un ejemplo de la necesidad de un 'método de tipo'. Estoy creando una de un conjunto de clases basadas en un código fuente XML. Entonces tengo un
función que se llama a su vez en cada clase.
La función debe ser estática, ya que de lo contrario perdemos tiempo creando objetos inapropiados. Como señala @Ian Boyde, podría hacerse en una clase de fábrica, pero esto solo agrega complejidad.
Sería bueno agregarlo a la interfaz para forzar a los implementadores de clase a implementarlo. Esto no causaría una sobrecarga significativa: es solo una comprobación del tiempo de compilación / enlace y no afecta a la tabla vtable.
Sin embargo, también sería una mejora bastante menor. Como el método es estático, yo como la persona que llama, debo llamarlo explícitamente y obtener un error de compilación inmediato si no se implementa. Permitir que se especifique en la interfaz significaría que este error se produce marginalmente antes en el ciclo de desarrollo, pero esto es trivial en comparación con otros problemas de interfaz rota.
Por lo tanto, es una característica potencial menor que, en general, es mejor dejar de lado.
fuente
El hecho de que Microsoft implemente una clase estática en C # creando una instancia especial de una clase con los elementos estáticos es solo una rareza de cómo se logra la funcionalidad estática. No es un punto teórico.
Una interfaz DEBE ser un descriptor de la interfaz de clase, o cómo se interactúa con ella, y eso debe incluir interacciones que sean estáticas. La definición general de interfaz (de Meriam-Webster): el lugar o área en la que las diferentes cosas se encuentran y se comunican o afectan entre sí. Cuando omite componentes estáticos de una clase o clases estáticas por completo, ignoramos grandes secciones de cómo interactúan estos chicos malos.
Aquí hay un ejemplo muy claro de dónde sería muy útil poder usar interfaces con clases estáticas:
Actualmente, escribo las clases estáticas que contienen estos métodos sin ningún tipo de verificación para asegurarme de que no haya olvidado nada. Es como los malos viejos tiempos de programación antes de OOP.
fuente
C # y el CLR deberían admitir métodos estáticos en las interfaces como lo hace Java. El modificador estático es parte de una definición de contrato y tiene un significado, específicamente que el comportamiento y el valor de retorno no varían según la instancia, aunque aún puede variar de una llamada a otra.
Dicho esto, le recomiendo que cuando quiera usar un método estático en una interfaz y no pueda, use una anotación en su lugar. Obtendrá la funcionalidad que está buscando.
fuente
Creo que la respuesta corta es "porque es de cero utilidad". Para llamar a un método de interfaz, necesita una instancia del tipo. Desde los métodos de instancia, puede llamar a cualquier método estático que desee.
fuente
Creo que la pregunta es llegar al hecho de que C # necesita otra palabra clave, precisamente para este tipo de situación. Desea un método cuyo valor de retorno depende solo del tipo en el que se llama. No puede llamarlo "estático" si dicho tipo es desconocido. Pero una vez que se conoce el tipo, se volverá estático. La idea es "estática no resuelta": todavía no es estática, pero una vez que sepamos el tipo de recepción, lo será. Este es un concepto perfectamente bueno, por eso los programadores siguen pidiéndolo. Pero no encajaba exactamente en la forma en que los diseñadores pensaban sobre el lenguaje.
Como no está disponible, he usado métodos no estáticos como se muestra a continuación. No es exactamente ideal, pero no puedo ver ningún enfoque que tenga más sentido, al menos no para mí.
fuente
Entonces, si desea acceder a los métodos de Contrato de interfaz, debe crear un objeto. Siempre es obligatorio que no esté permitido en el caso de los métodos estáticos. Las clases estáticas, el método y las variables nunca requieren objetos y se cargan en la memoria sin crear objetos de esa área (o clase) o puede decir que no requieren la creación de objetos.
fuente
Conceptualmente, no hay ninguna razón por la cual una interfaz no pueda definir un contrato que incluya métodos estáticos.
Para la implementación actual del lenguaje C #, la restricción se debe a la concesión de herencia de una clase base e interfaces. Si "class SomeBaseClass" implementa "interface ISomeInterface" y "class SomeDerivedClass: SomeBaseClass, ISomeInterface" también implementa la interfaz, un método estático para implementar un método de interfaz no se compilará porque un método estático no puede tener la misma firma que un método de instancia (lo cual estar presente en la clase base para implementar la interfaz).
Una clase estática es funcionalmente idéntica a un singleton y tiene el mismo propósito que un singleton con una sintaxis más limpia. Como un singleton puede implementar una interfaz, las implementaciones de interfaz por estática son conceptualmente válidas.
Por lo tanto, simplemente se reduce a la limitación del conflicto de nombres de C #, por ejemplo, y a los métodos estáticos del mismo nombre en la herencia. No hay ninguna razón por la cual C # no se pueda "actualizar" para admitir contratos de métodos estáticos (interfaces).
fuente
Cuando una clase implementa una interfaz, está creando una instancia para los miembros de la interfaz. Si bien un tipo estático no tiene una instancia, no tiene sentido tener firmas estáticas en una interfaz.
fuente