¿Por qué C # no permite que los métodos estáticos implementen una interfaz?


447

¿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;
    }

    ....

 }

66
Bueno, Java 8 lo tiene ( stackoverflow.com/questions/23148471/… ).
liang

1
Vea cómo puede combinar un comportamiento estático con la herencia o la implementación de la interfaz: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
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)
Jeroen Mostert

2
¡Solo dejo que todos sepan que la espera ha terminado! C # 8.0 tiene métodos de interfaz estática: dotnetfiddle.net/Lrzy6y (aunque funcionan un poco diferente a cómo OP quería que funcionaran, no tiene que implementarlos)
Jamie Twells

Respuestas:


221

Asumiendo que estás preguntando por qué no puedes 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 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.

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


66
Un ejemplo perfecto! Pero no estoy seguro de entender tu razonamiento. ¿Seguramente el compilador podría haber sido diseñado para mirar también a los miembros de statuc? ¿Las instancias no tienen una tabla de direcciones para la implementación de estos métodos? ¿No podrían incluirse los métodos estáticos en esta tabla?
Kramii

12
Hay un caso en el que esto podría ser útil. Por ejemplo, quiero que todos los implementadores implementen un método GetInstance que tome un argumento XElement. No puedo definir eso como un método estático en la interfaz, ni exigir una firma de constructor de la interfaz.
oleks

25
Mucha gente ha intervenido en ambos lados: desde "Esto no tiene sentido" hasta "Es un error, ojalá pudieras". (Creo que hay casos de uso válidos para ello, que es como terminé aquí). Como solución alternativa, el método de instancia simplemente puede delegar a un método estático.
harpo

55
También podría simplemente implementar la pieza como un método de extensión en la interfaz base, como en: 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.
Troy Alford el

8
Tendría mucho sentido con los genéricos. Por ejemplo, anular Something <T> () donde T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I

173

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 ):

... el principio de diseño central de los métodos estáticos, el principio que les da su nombre ... [es] ... siempre se puede determinar exactamente, en el momento de la compilación, qué método se llamará. Es decir, el método se puede resolver únicamente mediante un 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 "esto" 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 no se ha convencido de su utilidad.


55
Excelente, esta es la respuesta que quería escribir: simplemente no conocía los detalles de implementación.
Chris Marasti-Georg

55
Gran respuesta. ¡Y quiero este 'método de tipo'! Sería útil en muchas ocasiones (piense en metadatos para un tipo / clase).
Philip Daubmeier

23
Esta es la respuesta correcta. Si le pasa una interfaz a alguien, necesita saber cómo llamar a un método. Una interfaz es solo una tabla de método virtual . Tu clase estática no tiene eso. La persona que llama no sabría cómo llamar a un método. (Antes de leer esta respuesta que pensé C # sólo estaba siendo pedante. Ahora me doy cuenta que es una limitación técnica, impuesto por lo que una interfaz es ). Otras personas te hablarán sobre cómo es un mal diseño. No es un mal diseño, es una limitación técnica.
Ian Boyd

2
+1 por responder realmente la pregunta y no ser pedante y mocoso. Como dijo Ian Boyd: "No es un mal diseño, es una limitación técnica".
Joshua Pech

3
Es totalmente posible generar un objeto para una clase estática con vtable asociado. Observe cómo maneja Scala objectsy cómo se les permite implementar interfaces.
Sebastian Graf

97

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:

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 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:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

Para utilizar la creación de instancias para resolver el problema de la interfaz estática, debemos 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 demás métodos. ¡Obviamente, tampoco es una solución elegante!


35
+1 para "La mayoría de las respuestas aquí parecen perder todo el punto"; es increíble lo que parece que casi todas las respuestas esquivar el núcleo de la cuestión de ahondar en su mayoría verborrea inútil ...

55
@ Chris Aquí hay un ejemplo específico que me hizo volver a esta restricción. Quiero agregar una interfaz IResettable a las clases para indicar que almacenan en caché ciertos datos en variables estáticas que el administrador del sitio puede restablecer (por ejemplo, una lista de categorías de pedidos, un conjunto de tasas de IVA, una lista de categorías recuperadas de una API externa) para reducir el DB y los accesos externos a la API, esto obviamente usaría un restablecimiento de método estático. Esto me permite automatizar la detección de qué clases se pueden restablecer. Todavía puedo hacer esto, pero el método no se aplica o no se agrega automáticamente en el IDE y depende de la esperanza.
mattmanser

66
@ Chris No estoy de acuerdo, exageración masiva. Siempre es una señal de un defecto de lenguaje cuando más arquitectura es la "mejor" solución. ¿Recuerdas todos los patrones de los que ya nadie habla desde que C # obtuvo métodos genéricos y anónimos?
mattmanser

3
¿No puedes usar "where T: IQueryable, T: IDoSomeStaticMath" o similar?
Roger Willcocks

2
@ ChrisMarasti-Georg: una forma un tanto sucia pero interesante es esta pequeña construcción: public abstract class DBObject<T> where T : DBObject<T>, new()y luego hacer que todas las clases de DB hereden como DBObject<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 protegido String 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 tema
Nyerguds

20

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> () donde T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

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:

  1. 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 que contengan cosas relacionadas con ese tipo. Esta información podría proporcionarse haciendo que cada clase de interés llame a una rutina de "información de registro", o usando Reflection para obtener la información cuando se ejecuta el constructor estático de la variación de clase. Creo que este último enfoque es utilizado por cosas como Comparer <T> .Default ().
  2. Para cada clase T de interés, defina una clase o estructura que implemente IGetWhateverClassInfo <T> y satisfaga una restricción "nueva". La clase en realidad no contendrá ningún campo, 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 objeto descriptor cada vez. Si usa una estructura, el costo de creación de instancias debe 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 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).


16

Miopía, supongo.

Cuando se diseñaron originalmente, las interfaces solo se utilizaron con instancias de clase

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

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.


Es en el contexto de los genéricos que encontré el problema por primera vez, pero me pregunto si incluir métodos estáticos en las interfaces también podría ser útil en otros contextos. ¿Hay alguna razón por la cual las cosas no se pueden cambiar?
Kramii

Yo también encuentro esto al implementar una clase genérica que requiere que el tipo de parámetro se cree con algunos parámetros. Dado que new () no puede tomar ninguna. ¿Ya descubriste cómo hacer esto, Kramii?
Tom

1
@Kramii: Contratos para API estáticas. No quiero una instancia de objeto, solo una garantía de una firma particular, por ejemplo. IMatrixMultiplier o ICustomSerializer. Funcs / Actions / Delegates como miembros de la clase hacen el truco, pero IMO esto a veces parece exagerado, y puede ser confuso para los inexpertos que intentan extender la API.
David Cuccia

14

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.


71
Lo siento ... no estoy seguro de que sea así. Una interfaz no especifica el comportamiento. Una interfaz define un conjunto de operaciones con nombre. Dos clases podrían implementar un método de interfaz para comportarse de maneras completamente diferentes. Entonces, una interfaz no especifica el comportamiento en absoluto. La clase que lo implementa lo hace.
Scott Langham el

1
Espero que no creas que soy exigente ... pero creo que es una distinción importante para cualquiera que esté aprendiendo OO a entender.
Scott Langham el

44
Se supone que una interfaz especifica un contrato que incluye comportamiento y presentación. Es por eso que cambiar el comportamiento de una llamada de interfaz es un no-no, ya que se supone que ambos deben arreglarse. Si tiene una interfaz donde la llamada actúa de manera diferente (es decir, IList.Add realizó una eliminación), no sería correcto.
Jeff Yates el

14
Bueno, sí, estaría deformado en la cabeza para definir un método para comportarse de manera incongruente con su nombre. Sin embargo, si tuviera IAlertService.GetAssistance (), su comportamiento podría ser encender una luz, hacer sonar una alarma o golpear el ojo con un palo.
Scott Langham, el

1
Una implementación también podría escribir en un archivo de registro. Este comportamiento no se especifica en la interfaz. Pero, tal vez tengas razón. El comportamiento para 'obtener asistencia' realmente debe ser respetado.
Scott Langham, el

14

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.


2
Estoy totalmente de acuerdo con esta respuesta simple pero efectiva. Lo que sería interesante en la "interfaz estática" es que representaría un contrato. Tal vez no debería llamarse una "interfaz estática", pero todavía echamos de menos una construcción. Por ejemplo, consulte el documento oficial de .NET sobre la interfaz ICustomMarshaler. Requiere que la clase que lo implementa "agregue un método estático llamado GetInstance que acepte un String como parámetro y tenga un tipo de retorno de ICustomMarshaler". Eso realmente parece una definición de "interfaz estática" en inglés simple, mientras que preferiría que en C # ...
Simon Mourier

@SimonMourier Esa documentación podría haberse escrito más claramente, pero la está malinterpretando. No es la interfaz ICustomMarshaler la que requiere el método estático GetInstance. Es el atributo de código [MarshalAs] el que requiere esto. Están utilizando el patrón de fábrica para permitir que el atributo obtenga una instancia del marshaler adjunto. Desafortunadamente, se olvidaron por completo de incluir la documentación del requisito de GetInstance en la página de documentación de MarshalAs (solo muestra ejemplos que utilizan implementaciones de cálculo de referencias incorporadas).
Scott Gartner

@ScottGartner - No entiendas tu punto. msdn.microsoft.com/en-us/library/… establece claramente: "Además de implementar la interfaz ICustomMarshaler, los marshalers personalizados deben implementar un método estático llamado GetInstance que acepta un String como parámetro y tiene un tipo de retorno de ICustomMarshaler. Esto la capa de interoperabilidad COM de Common Language Runtime llama al método estático para crear una instancia de la herramienta de cálculo de referencias personalizada ". Esta es definitivamente una definición de contrato estático.
Simon Mourier

9

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?


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;  

1
Otros lenguajes (Java, por ejemplo) permiten que se invoquen métodos estáticos desde instancias de objetos, aunque recibirá una advertencia de que debería llamarlos desde un contexto estático.
Chris Marasti-Georg

Permitir que se llame a un método estático desde una instancia también está permitido en .Net. Eso es algo diferente. Si un método estático implementara una interfaz, podría llamarla SIN una instancia. ESO es lo que no tiene sentido. Recuerde que no puede poner la implementación en una interfaz, debe estar en la clase. Entonces, si se definieron cinco clases diferentes para implementar esa interfaz, y cada una tenía una implementación diferente de este método estático, ¿cuál usaría el compilador?
Charles Bretana

1
@CharlesBretana en el caso de los genéricos, el del tipo que se pasa. Veo mucha utilidad en tener interfaces para métodos estáticos (o si lo desea, llamémoslas "interfaces estáticas" y permita que una clase defina ambas interfaces estáticas). e interfaces de instancia). Entonces, si tengo Whatever<T>() where T:IMyStaticInterface, podría llamar Whatever<MyClass>()y tener T.MyStaticMethod()dentro de la Whatever<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.
Jcl

4

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:

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 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:

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


Los operadores en C # son estáticos, por lo que esto todavía no tiene sentido.
John Saunders

Ese es el punto, la restricción verifica que el TIPO (no instancia) tiene el operador + (y por extensión el operador + =) y permite que se genere la plantilla, luego, cuando la sustitución real de la plantilla se produce, se garantiza que el objeto que se está utilizando de un tipo que tiene ese operador (u otro método estático). Entonces puede decir: SumElements <int> (0, 5); que instanciaría esto: SumElements (int initVal, int [] values) {foreach (var v in values) {initVal + = v; }}, que por supuesto, tiene mucho sentido
Jeremy Sorensen

1
Recuerda que no es una plantilla. Es genérico, que no es lo mismo que las plantillas de C ++.
John Saunders

@ JohnSaunders: los genéricos no son plantillas, y no sé cómo se podría hacer que funcionen razonablemente con operadores vinculados estáticamente, pero en contextos genéricos hay muchos casos en los que podría ser útil especificar que un Tdebería tener una estática miembro (por ejemplo, una fábrica que produce Tinstancias). 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 ...
supercat

1
@JohnSaunders: El problema no es tanto que el método implemente una interfaz, sino que el compilador no puede seleccionar un método virtual para llamar sin tener una instancia de objeto sobre la cual basar la selección. Una solución es despachar la llamada de la "interfaz estática" sin usar un despacho virtual (que no funcionará) sino usar una llamada a una clase estática genérica. El envío genérico basado en tipos no requiere tener una instancia, sino simplemente tener un tipo.
supercat

3

Porque las interfaces están en la estructura de herencia, y los métodos estáticos no heredan bien.


3

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.


Curiosamente, es bastante posible llamar a un método estático con ambos Type ad Instance en VB.Net (aunque el IDE da una advertencia en este último caso). No parece ser un problema. Puede que tengas razón sobre las optimizaciones.
Kramii

3

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.


Este es un POV interesante, y es probablemente el que los diseñadores de C # tenían en mente. Pensaré en los miembros estáticos de una manera diferente a partir de ahora.
Kramii el

3

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:

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


1

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.


1

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.


1
Sí, una declaración estática en la interfaz sería una tontería. No se debe obligar a una clase a implementar la interfaz de cierta manera. Sin embargo, C # restringe una clase para implementar la interfaz utilizando métodos no estáticos. ¿Esto tiene sentido? ¿Por qué?
Kramii

No puede usar la palabra clave 'estática'. Sin embargo, no hay restricción porque no necesita la palabra clave estática para escribir un método que se comporte estáticamente. Simplemente escriba un método que no acceda a ninguno de los miembros de su objeto, luego se comportará como un método estático. Entonces, hay restricción.
Scott Langham el

Verdadero Scott, pero no permite que alguien acceda a ese método de forma estática, en otra parte del código. No es que se me ocurra una razón por la que querrías hacerlo, pero eso parece ser lo que pregunta el OP.
Chris Marasti-Georg

Bueno, si realmente sintió la necesidad, podría escribirlo como un método estático para acceder en otra parte del código, y simplemente escribir el método no estático para llamar al método estático. Podría estar equivocado, pero dudo que sea un objetivo del interrogador.
Scott Langham, el

1

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.


1

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


2
El problema es que esta pregunta no se trata de "en OOP". Se trata de "en C #". En C #, no hay "mensajes".
John Saunders

1

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

  static public bool IsHandled(XElement xml)

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.


1

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:

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 verificación para asegurarme de que no haya olvidado nada. Es como los malos viejos tiempos de programación antes de OOP.


Esta es una pregunta antigua; En estos días, tratamos de evitar las preguntas basadas en opiniones porque son difíciles de responder con autoridad. La única buena manera de responder a esto sería describir las razones por las que MS tuvo, o debería haber tenido, para hacerlo de la manera que lo hicieron.
Nathan Tuggy

1

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.


0

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.


0

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

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

Según el concepto orientado a objetos Interfaz implementada por clases y tienen contrato para acceder a estas funciones implementadas (o métodos) utilizando objetos.

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.


0

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


-1

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.