c# - una - interfaz asp net
¿Por qué C#no permite que los métodos estáticos implementen una interfaz? (24)
Según el concepto Orientado a Objetos, la Interfaz implementada por clases y tiene contrato para acceder a estas funciones (o métodos) implementados usando objetos.
Por lo tanto, si desea acceder a los métodos de Contrato de interfaz, debe crear un objeto. Siempre es obligatorio que no esté permitido en caso de 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 el objeto de esa área (o clase) o puede decir que no requiere la creación de objetos.
¿Por qué C # fue diseñado de esta manera?
Como lo entiendo, una interfaz solo describe el comportamiento, y sirve para describir una obligación contractual para las clases que implementan la interfaz en la 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;
}
....
}
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 sería posible 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 en uno genérico. En este caso, la interfaz no es una interfaz en absoluto, 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:
T SumElements<T>(T initVal, T[] values)
{
foreach (var v in values)
{
initVal += v;
}
}
Aquí no hay polimorfismo, el genérico usa el tipo real del objeto y llama al operador + =, pero esto falla porque no puede decir con seguridad 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 necesita C # es un tipo de restricción real, todas las interfaces también serían restricciones, pero no todas las restricciones serían interfaces, entonces usted podría hacer esto:
constraint CHasPlusEquals
{
static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}
T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
foreach (var v in values)
{
initVal += v;
}
}
Ya se ha hablado mucho acerca de cómo realizar una aritmética para todos los tipos numéricos para implementar, pero existe preocupación por la eficiencia, ya que una restricción no es una construcción polimórfica, por lo que una restricción CArithmetic resolvería ese problema.
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á llamando. no tiene sentido permitir que un método estático implemente la interfaz,
¿Cómo lo llamarías?
public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
public static void MyMethod() { //Do Something; }
}
// inside of some other class ...
// How would you call the method on the interface ???
MyClass.MyMethod(); // this calls the method normally
// not through the interface...
// This next fails you can''t cast a classname to a different type...
// Only instances can be Cast to a different type...
MyInterface myItf = MyClass as MyInterface;
Debido a que las interfaces están en la estructura de herencia, y los métodos estáticos no se heredan bien.
El hecho de que Microsoft esté implementando una clase estática en C # al crear 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 de cómo interactúa, y eso debería incluir interacciones que sean estáticas. La definición general de interfaz (de Meriam-Webster): el lugar o área en que las diferentes cosas se encuentran, se comunican o se afectan entre sí. Cuando omites los componentes estáticos de una clase o clases estáticas por completo, estamos ignorando grandes secciones de cómo interactúan estos chicos malos.
Este es un ejemplo muy claro de dónde sería muy útil poder usar interfaces con clases estáticas:
public interface ICrudModel<T, Tk>
{
Boolean Create(T obj);
T Retrieve(Tk key);
Boolean Update(T obj);
Boolean Delete(T obj);
}
Actualmente, escribo las clases estáticas que contienen estos métodos sin ningún tipo de comprobación para asegurarme de que no he olvidado nada. Es como los viejos tiempos de la programación antes de OOP.
En la medida en que las interfaces representan "contratos", parece bastante razonable que las clases estáticas implementen interfaces.
Todos los argumentos anteriores parecen pasar por alto este punto sobre los contratos.
FYI: Podría obtener un comportamiento similar al que desea creando 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 sería parte del contrato.
La mayoría de la gente parece olvidar que en las Clases de POO también son objetos, y por eso tienen mensajes, que por alguna razón c # llaman "método estático". El hecho de que existan diferencias entre los objetos de instancia y los objetos de clase solo muestra fallas o defectos en el lenguaje. Optimista sobre c # aunque ...
La mayoría de las respuestas aquí parecen pasar por alto todo el punto. El polimorfismo se puede utilizar no solo entre instancias, sino también entre tipos. Esto es a menudo necesario, cuando usamos genéricos.
Supongamos que tenemos un parámetro de tipo en el método genérico y necesitamos hacer alguna operación con él. No queremos crear una instancia instantánea, porque no conocemos a los constructores.
Por ejemplo:
Repository GetRepository<T>()
{
//need to call T.IsQueryable, but can''t!!!
//need to call T.RowCount
//need to call T.DoSomeStaticMath(int param)
}
...
var r = GetRepository<Customer>()
Desafortunadamente, solo se me ocurren alternativas "feas":
Usa la reflexión Ugly y supera la idea de interfaces y polimorfismo.
Crea una clase de fábrica completamente separada.
Esto podría aumentar enormemente la complejidad del código. Por ejemplo, si estamos intentando modelar objetos de dominio, cada objeto necesitaría otra clase de repositorio.
Instalar y luego llamar al método de interfaz deseado
Esto puede ser difícil de implementar incluso si controlamos la fuente de las clases, utilizadas como parámetros genéricos. La razón es que, por ejemplo, podríamos necesitar que las instancias estén solo en un estado conocido, "conectado a DB".
Ejemplo:
public class Customer
{
//create new customer
public Customer(Transaction t) { ... }
//open existing customer
public Customer(Transaction t, int id) { ... }
void SomeOtherMethod()
{
//do work...
}
}
Para usar la instantinación para resolver el problema de la interfaz estática, necesitamos hacer lo siguiente:
public class Customer: IDoSomeStaticMath
{
//create new customer
public Customer(Transaction t) { ... }
//open existing customer
public Customer(Transaction t, int id) { ... }
//dummy instance
public Customer() { IsDummy = true; }
int DoSomeStaticMath(int a) { }
void SomeOtherMethod()
{
if(!IsDummy)
{
//do work...
}
}
}
Esto es obviamente feo y también innecesario complica el código para todos los otros métodos. Obviamente, tampoco es una solución elegante!
Las clases estáticas deberían poder hacer esto para que puedan usarse de forma genérica. Tuve que implementar un Singleton para lograr los resultados deseados.
Tuve un montón de clases de Capa de negocio estático que implementaron métodos CRUD como "Crear", "Leer", "Actualizar", "Eliminar" para cada tipo de entidad como "Usuario", "Equipo", ect .. Luego creé una base control que tenía una propiedad abstracta para la clase Business Layer que implementó 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.
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.
Las interfaces son conjuntos abstractos de funcionalidades definidas disponibles.
Si un método en esa interfaz se comporta o no como estático o no, es un detalle de implementación que debe estar oculto detrás de la interfaz . Sería incorrecto definir un método de interfaz como estático porque estaría forzando innecesariamente a que el método se implemente 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 una buena cosa por la que esforzarse en el diseño orientado a objetos (no voy a entrar en por qué, puedes leerlo aquí: http://en.wikipedia.org/wiki/Object-oriented ) Por esta razón, los métodos estáticos no están permitidos en las interfaces.
Lo que parece querer permitiría que se invocara un método estático a través del Tipo o cualquier instancia de ese tipo. Esto, como mínimo, daría lugar a una ambigüedad que no es un rasgo deseable.
Habría debates interminables sobre si importaba, cuál es la mejor práctica y si hay problemas de desempeño al hacerlo de una manera u otra. Simplemente no soportándolo C # nos ahorra tener que preocuparnos por eso.
También es probable que un compilador que se ajuste a este deseo pierda algunas optimizaciones que pueden venir con una separación más estricta entre la instancia y los métodos estáticos.
Mi razón técnica (simplificada) es que los métodos estáticos no están en vtable , y el sitio de 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 más detalles, necesitarías un graduado de CS o un wonk del compilador, de los cuales no soy ninguno.
Por razones políticas, citaré a Eric Lippert (que es un won de compilación y tiene una Licenciatura en Matemáticas, Informática y Matemáticas Aplicadas de la Universidad de Waterloo (fuente: LinkedIn ):
... el principio de diseño básico de los métodos estáticos, el principio que les da su nombre ... [es] ... siempre se puede determinar exactamente, en tiempo de compilación, a qué método se llamará. Es decir, el método puede resolverse únicamente mediante el análisis estático del código.
Tenga en cuenta que Lippert deja espacio para el llamado método de tipo:
Es decir, un método asociado con un tipo (como un estático), que no toma un argumento de "este" no anulable (a diferencia de una instancia o virtual), pero uno donde el método llamado dependería del tipo construido de T ( a diferencia de una estática, que debe ser determinable en tiempo de compilación).
pero aún está por convencerse de su utilidad.
Miopía, supongo.
Cuando se diseñaron originalmente, las interfaces solo estaban destinadas a usarse con instancias de clase
IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();
Fue solo con la introducción de interfaces, ya que las restricciones para genéricos hicieron que la adición de un método estático a una interfaz tuviera un uso práctico.
(respondiendo al comentario :) Creo que cambiarlo ahora requeriría un cambio en el CLR, lo que llevaría a incompatibilidades con los ensamblajes existentes.
Para dar un ejemplo en el que me estoy perdiendo 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 un almacenamiento de base de datos, tenemos una clase genérica 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 de una fila de la base de datos y que debe implementar una interfaz IDataRow. Incluido en el IDataRow es una descripción de la estructura de la tabla para leer de la base de datos. El DataTable debe solicitar la estructura de datos de IDataRow antes de leer del DB. Actualmente esto se ve como:
interface IDataRow {
string GetDataSTructre(); // How to read data from the DB
void Read(IDBDataRow); // How to populate this datarow from DB data
}
public class DataTable<T> : List<T> where T : IDataRow {
public string GetDataStructure()
// Desired: Static or Type method:
// return (T.GetDataStructure());
// Required: Instantiate a new class:
return (new T().GetDataStructure());
}
}
GetDataStructure solo se requiere una vez para que cada tabla lea, la sobrecarga para crear una instancia más es mínima. Sin embargo, sería bueno en este caso aquí.
Puede pensar que los métodos estáticos y los métodos no estáticos de una clase son interfaces diferentes. Cuando se los 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 trata. Por lo tanto, si utiliza 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.
Sé 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:
string DoSomething<T>() where T:ISomeFunction { if (T.someFunction()) ... }
El simple hecho de tener métodos estáticos para implementar 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:
- Cree una clase genérica estática cuyo parámetro de tipo será el tipo que pasaría a DoSomething arriba. Cada variación de esta clase tendrá uno o más miembros estáticos con contenido relacionado con ese tipo. Esta información podría suministrarse haciendo que cada clase de interés llame a una rutina de "información de registro", o utilizando Reflection para obtener la información cuando se ejecute el constructor estático de la variación de clase. Creo que este último enfoque es usado por cosas como Comparer <T> .Default ().
- Para cada clase T de interés, defina una clase o estructura que implemente IGetWhateverClassInfo <T> y satisfaga una restricción "nueva". La clase no contendrá ningún campo en realidad, pero tendrá una propiedad estática que devuelve un campo estático con la información de tipo. Pase el tipo de esa clase o estructura a la rutina genérica en cuestión, que podrá crear una instancia y usarla para obtener información sobre la otra clase. Si usa una clase para este propósito, probablemente debería definir una clase genérica estática como se indicó anteriormente, para evitar tener que construir una nueva instancia de descriptor-objeto cada vez. Si utiliza una estructura, el costo de creación de instancias debería ser nulo, pero cada tipo de estructura diferente requeriría una expansión diferente de la rutina DoSomething.
Ninguno de estos enfoques es realmente atractivo. Por otro lado, esperaría que si los mecanismos existían 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 en particular parece para ser comparable en dificultad a saber si tiene un método estático con una firma particular).
Suponiendo que esté preguntando por qué no puede hacer esto:
public interface IFoo {
void Bar();
}
public class Foo: IFoo {
public static void Bar() {}
}
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 que su implementación podría volverse 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 permitiría acceder a ella desde un contexto estático, y devolvería ese valor en la implementación.public class Animal: IListItem {
/* Can be tough to come up with a different, yet meaningful name!
* A different casing convention, like Java has, would help here.
*/
public const string AnimalScreenName = "Animal";
public string ScreenName(){ return AnimalScreenName; }
}
Para una situación más complicada, siempre puede declarar otro método estático y delegar a eso. Al intentar dar un ejemplo, no puedo pensar en ninguna razón por la que haría algo no trivial tanto en un contexto estático como de instancia, así que le ahorraré un blob de FooBar y lo tomaría como una indicación de que podría no sea una buena idea
C # y 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 puede variar de una llamada a otra.
Dicho esto, 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 busca.
Creo que la pregunta está en el hecho de que C # necesita otra palabra clave, precisamente para este tipo de situación. Desea un método cuyo valor de retorno solo dependa del tipo al que se llama. No puede llamarlo "estático" si dicho tipo es desconocido. Pero una vez que el tipo se haga conocido, se volverá estático. La idea es "estática sin resolver", aún no estática, pero una vez que sepamos el tipo de recepción, lo será. Este es un concepto perfectamente bueno, por lo que los programadores siguen pidiéndolo. Pero no encajaba con la manera en que los diseñadores pensaban sobre el lenguaje.
Como no está disponible, he tomado la decisión de usar métodos no estáticos de la forma que se muestra a continuación. No es exactamente lo ideal, pero no veo ningún enfoque que tenga más sentido, al menos no para mí.
public interface IZeroWrapper<TNumber> {
TNumber Zero {get;}
}
public class DoubleWrapper: IZeroWrapper<double> {
public double Zero { get { return 0; } }
}
Creo que la respuesta corta es "porque es de utilidad cero". 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.
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.
OK, aquí hay un ejemplo de la necesidad de un ''método de tipo''. Estoy creando una de un conjunto de clases basadas en algún código fuente XML. Así que tengo un
static public bool IsHandled(XElement xml)
Función que se llama a su vez en cada clase.
La función debe ser estática, de lo contrario, perderemos tiempo creando objetos inapropiados. Como @Ian Boyde señala que 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 la clase a implementarlo. Esto no causaría una sobrecarga significativa, es solo una verificación de tiempo de compilación / enlace y no afecta a vtable.
Sin embargo, también sería una mejora bastante menor. Como el método es estático, yo, como el que llama, debo llamarlo explícitamente y así 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 ligeramente 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 dejarla fuera.