¿Por qué las interfaces de C # no pueden contener campos?

223

Por ejemplo, supongamos que quiero una ICarinterfaz y que todas las implementaciones contendrán el campo Year. ¿Esto significa que cada implementación tiene que declarar por separado Year? ¿No sería mejor simplemente definir esto en la interfaz?

deltanovember
fuente
21
Las interfaces no tienen implementación, para esto se usa una clase abstracta, con la propiedad Año
PostMan
66
Para agregar a lo que se ha dicho aquí, las interfaces son contratos, y un campo es un detalle de implementación, ya que define una ranura en la memoria de la máquina para poner un valor (ya sea escalar o puntero de dirección).
herzmeister el
55
Pero si el campo es público, es parte del contrato y no solo un detalle de implementación, ¿verdad?
Alex

Respuestas:

238

Aunque muchas de las otras respuestas son correctas a nivel semántico, también me parece interesante abordar este tipo de preguntas desde el nivel de detalles de implementación.

Una interfaz puede considerarse como una colección de ranuras , que contienen métodos . Cuando una clase implementa una interfaz, se requiere que la clase le diga al tiempo de ejecución cómo completar todos los espacios requeridos. Cuando tu dices

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

la clase dice "cuando creas una instancia de mí, escribe una referencia a Foo.M en el espacio para IFoo.M.

Luego, cuando haces una llamada:

IFoo ifoo = new Foo();
ifoo.M();

el compilador genera un código que dice "pregunte al objeto qué método hay en la ranura para IFoo.M, y llame a ese método.

Si una interfaz es una colección de ranuras que contienen métodos, entonces algunas de esas ranuras también pueden contener los métodos get y set de una propiedad, los métodos get y set de un indexador y los métodos add y remove de un evento. Pero un campo no es un método . No hay un "espacio" asociado con un campo que luego pueda "completar" con una referencia a la ubicación del campo. Y, por lo tanto, las interfaces pueden definir métodos, propiedades, indexadores y eventos, pero no campos.

Eric Lippert
fuente
26
Lo único que a veces echo de menos es una capacidad similar a Java para definir constantes de nivel de interfaz, que presumiblemente no requeriría una "ranura" para admitir en el idioma.
LBushkin
2
Me gusta la explicación en palabras simples. Gracias. "CLR a través de C #" y "Essential .net volume 1" proporcionan más detalles.
Sandeep GB
66
¿Por qué un campo no tiene un espacio? y la misma pregunta con los operadores? Recuerdo haber escuchado sobre el tipeo de patos usando la reflexión para ver si se implementa una interfaz, incluso si la clase no heredó la interfaz. ¿Por qué no se puede usar la reflexión (o una ranura) para levantar un campo? Todavía estoy escribiendo mi código, por lo que es posible que no necesite / desee campos, pero me sorprendió descubrir que no puedo usar operadores. operadores son exactamente iguales a los métodos de mi entendimiento, excepto que no todos se pueden sobrecargar ( An interface cannot contain constants, fields, operatorsDe. msdn.microsoft.com/en-us/library/ms173156.aspx )
@acidzombie: La pregunta de por qué una interfaz no puede definir operadores es diferente (aunque tal vez esté relacionada) de por qué no puede contener campos; Sugeriría publicar otra pregunta si todavía está interesado en eso.
Adam Robinson
1
@ b1nary.atr0phy ¿por qué con los campos? Por ejemplo, si declaro un método int One(), entonces la implementación public int One(){return 1;}no es un campo.
Hola Angel
131

Las interfaces en C # están destinadas a definir el contrato al que se adherirá una clase, no una implementación particular.

En ese espíritu, las interfaces de C # permiten que se definan propiedades, que la persona que llama debe proporcionar una implementación para:

interface ICar
{
    int Year { get; set; }
}

Las clases de implementación pueden usar propiedades automáticas para simplificar la implementación, si no hay una lógica especial asociada con la propiedad:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
LBushkin
fuente
55
¿No es todo lo público parte del contrato? Si una clase tiene Public int Year, ¿no dice que el contrato de la clase tiene un campo de tipo Year para estar presente y accesible?
Didier A.
1
Tarde a la fiesta, pero no, en este caso significa que el contrato tiene un Año de PROPIEDAD que se supone que debe implementar cualquier clase permanente. Las propiedades son en realidad métodos get / set, que tienen un campo de respaldo generado automáticamente si no se necesita una lógica especial. La sintaxis especial es solo para una notación más clara.
user3613916
¿Cómo puedo definir un valor predeterminado constante (como 123) para esa implementación automática de Year?
lama12345
1
@ lama12345 También llego tarde a la fiesta, pero desde C # 6 (2015, .NET Framework 4.6 y .NET Core) puede usar una propiedad automática para ese propósito. public int Year => 123;. Sin embargo, en este caso no tiene sentido tener un setter, por lo que la interfaz debería definirse conint Year { get; }
EriF89
56

Declararlo como una propiedad:

interface ICar {
   int Year { get; set; }
}
Tarydon
fuente
47
La pregunta es " ¿Por qué las interfaces C # no pueden contener campos?". Esto no aborda eso.
AakashM
14
Estoy de acuerdo en que esta respuesta no responde la pregunta de OP, pero bueno, resolvió mi problema.
Gorgen
36

Eric Lippert lo clavó, usaré una forma diferente de decir lo que dijo. Todos los miembros de una interfaz son virtuales y todos deben ser anulados por una clase que herede la interfaz. No escribe explícitamente la palabra clave virtual en la declaración de la interfaz, ni utiliza la palabra clave de anulación en la clase, están implícitas.

La palabra clave virtual se implementa en .NET con métodos y una denominada v-table, una matriz de punteros de métodos. La palabra clave override llena el espacio de la tabla v con un puntero de método diferente, sobrescribiendo el producido por la clase base. Las propiedades, los eventos y los indexadores se implementan como métodos subyacentes. Pero los campos no lo son. Por lo tanto, las interfaces no pueden contener campos.

Hans Passant
fuente
Usted proporcionó el nombre técnico / real (v-table) del concepto de tragamonedas mencionado por Eric. Gracias por el detalle Hans.
RBT
¿Cuál sería el punto de una tabla v si las interfaces no son implementaciones predeterminadas? Esto cambia en C # 8.0, pero eso no viene al caso.
AnthonyMonterrosa
19

¿Por qué no simplemente tener una Yearpropiedad, que está perfectamente bien?

Las interfaces no contienen campos porque los campos representan una implementación específica de representación de datos, y exponerlos rompería la encapsulación. Por lo tanto, tener una interfaz con un campo sería una codificación efectiva para una implementación en lugar de una interfaz, ¡lo cual es una paradoja curiosa para una interfaz!

Por ejemplo, parte de su Yearespecificación podría requerir que no sea válido para los ICarimplementadores permitir la asignación a un año Yearposterior al año actual + 1 o anterior a 1900. No hay forma de decir que si hubiera expuesto Yearcampos, es mucho mejor usar propiedades en lugar de hacer el trabajo aquí.

John Feminella
fuente
18

La respuesta corta es sí, cada tipo de implementación tendrá que crear su propia variable de respaldo. Esto se debe a que una interfaz es análoga a un contrato. Todo lo que puede hacer es especificar partes particulares de código de acceso público que un tipo de implementación debe poner a disposición; no puede contener ningún código en sí mismo.

Considere este escenario usando lo que sugiere:

public interface InterfaceOne
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public interface InterfaceTwo
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public class MyClass : InterfaceOne, InterfaceTwo { }

Tenemos un par de problemas aquí:

  • Debido a que todos los miembros de una interfaz son, por definición, públicos, nuestra variable de respaldo ahora está expuesta a cualquiera que use la interfaz
  • ¿Qué myBackingVariableva a MyClassusar?

El enfoque más común adoptado es declarar la interfaz y una clase abstracta básica que la implementa. Esto le permite la flexibilidad de heredar de la clase abstracta y obtener la implementación de forma gratuita, o implementar explícitamente la interfaz y se le permite heredar de otra clase. Funciona algo como esto:

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}
Adam Robinson
fuente
7

Otros han dado el 'Por qué', así que solo agregaré que su interfaz puede definir un Control; si lo envuelve en una propiedad:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}
zomf
fuente
3

Las interfaces no contienen ninguna implementación.

  1. Definir una interfaz con una propiedad.
  2. Además, puede implementar esa interfaz en cualquier clase y utilizar esta clase en el futuro.
  3. Si es necesario, puede tener esta propiedad definida como virtual en la clase para que pueda modificar su comportamiento.
Amit
fuente
3

Ya se ha dicho mucho, pero para simplificar, aquí está mi opinión. Las interfaces tienen la intención de tener contratos de métodos para ser implementados por los consumidores o clases y no tener campos para almacenar valores.

Puede argumentar que entonces ¿por qué se permiten las propiedades? Entonces, la respuesta simple es: las propiedades se definen internamente solo como métodos.

Prakash Tripathi
fuente
1
Si necesita acceder a un miembro, conviértalo en una propiedad y estará bien.
Unome
0

Para esto, puede tener una clase base Car que implemente el campo año, y todas las demás implementaciones pueden heredar de él.


fuente
0

Una interfaz define propiedades y métodos de instancia pública . Los campos son generalmente privados, o como máximo protegidos, internos o internos protegidos (el término "campo" generalmente no se usa para nada público).

Como se indica en otras respuestas, puede definir una clase base y definir una propiedad protegida a la que puedan acceder todos los herederos.

Una rareza es que, de hecho, una interfaz puede definirse como interna, pero limita la utilidad de la interfaz, y normalmente se usa para definir la funcionalidad interna que no utiliza otro código externo.

Frode N. Rosand
fuente