¿Por qué los parámetros opcionales C # 4 definidos en la interfaz no se aplican en la clase de implementación?

361

Noté que con los parámetros opcionales en C # 4 si especifica un parámetro opcional en una interfaz que no tiene, debe hacer que ese parámetro sea opcional en cualquier clase de implementación:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

y por lo tanto:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

¿Alguien sabe por qué los parámetros opcionales están diseñados para funcionar de esta manera?

Por un lado, supongo que la capacidad de anular cualquier valor predeterminado especificado en las interfaces es útil, aunque para ser honesto, no estoy seguro de si incluso debería poder especificar los valores predeterminados en la interfaz, ya que eso debería ser una decisión de implementación.

Por otro lado, esta desconexión significa que no siempre se puede usar la clase concreta y la interfaz de manera intercambiable. Esto, por supuesto, no sería un problema si el valor predeterminado se especifica en la implementación, pero si está exponiendo su clase concreta como la interfaz (usando un marco IOC para inyectar la clase concreta, por ejemplo), entonces realmente no hay punto tener el valor predeterminado ya que la persona que llama tendrá que proporcionarlo siempre de todos modos.

themon ardiente
fuente
22
¿Porque son opcionales?
Oded
1
Pero usted puede echar la instancia del objeto a MyInterfacey llamarlo con el parámetro opcional: ((MyInterface)obj).TestMethod();.
Jim Mischel
77
@oded, pero si dice que este parámetro es opcional en el contrato, ¿por qué permite que el implementador no lo haga opcional? ¿eso no causa confusión a cualquiera que quiera usar el contrato?
theburningmonk
1
Creo que en este caso puede decir que el parámetro es opcional en la implementación, no en llamar a los métodos de implementación. Cuando llama al método en la clase, debe seguir las reglas de la clase (el parámetro no es opcional en la clase para que pueda no llame al método sin él), y en la segunda mano cuando implemente la interfaz, debe seguir las reglas de la interfaz, para que pueda anular los métodos con / sin parámetros opcionales. Solo una opinión.
Mohamad Alhamoud
2
Más detalles sobre la explicación del problema aquí -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

Respuestas:

236

ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog el 12 de mayo de 2011. ¡Gracias por la gran pregunta!

Suponga que tiene una interfaz como la describe y cien clases que la implementan. Luego decide hacer que uno de los parámetros de uno de los métodos de la interfaz sea opcional. ¿Está sugiriendo que lo correcto es que el compilador obligue al desarrollador a encontrar todas las implementaciones de ese método de interfaz y que el parámetro también sea opcional?

Supongamos que hicimos eso. Ahora suponga que el desarrollador no tiene el código fuente para la implementación:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

¿Cómo se supone que el autor de D hará que esto funcione? ¿Están obligados en su mundo a llamar al autor de B por teléfono y pedirles que les envíen una nueva versión de B que haga que el método tenga un parámetro opcional?

Eso no va a volar. ¿Qué pasa si dos personas llaman al autor de B, y una de ellas quiere que el valor predeterminado sea verdadero y una de ellas quiere que sea falso? ¿Qué pasa si el autor de B simplemente se niega a seguir el juego?

Quizás en ese caso se les exigiría decir:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

La característica propuesta parece agregar muchos inconvenientes para el programador sin un aumento correspondiente en el poder representativo. ¿Cuál es el beneficio convincente de esta función que justifica el mayor costo para el usuario?


ACTUALIZACIÓN: en los comentarios a continuación, supercat sugiere una función de lenguaje que realmente agregaría poder al lenguaje y permitiría algunos escenarios similares al descrito en esta pregunta. Para su información, esa característica, implementaciones predeterminadas de métodos en interfaces, se agregará a C # 8.

Eric Lippert
fuente
99
@supercat: No tenemos planes de hacerlo, pero una característica como esa es posible. Ya ha sido propuesto antes. Durante el proceso de diseño para C # 4, analizamos muchas de las características que denominamos "extensión de todo": tenemos métodos de extensión, entonces, ¿qué significaría tener eventos de extensión, propiedades de extensión, constructores de extensión, interfaces de extensión, etc. . Las clases que podría "adjuntar" a las interfaces y decir "estos métodos son las implementaciones predeterminadas de esta interfaz" son una forma posible de caracterizar las "interfaces de extensión". Una idea interesante
Eric Lippert el
16
para aclarar mi razonamiento de por qué creo que no es intuitivo: para mí, un parámetro opcional es "opcional para la persona que llama del método" NO "opcional para el implementador de la interfaz".
Stefan de Kok
8
"¿Están obligados en su mundo a llamar al autor de B por teléfono y pedirles que les envíen una nueva versión [...]?" No, no se requieren llamadas telefónicas. B simplemente ya no debería poder considerarse una implementación de MyInterface y el autor de D podría darse cuenta de eso por el compilador. Se requeriría que el autor de D implemente D con un TestMethod que acepte un parámetro predeterminado, ya que eso es lo que requiere el autor de la interfaz: está argumentando en contra de permitir que el autor de la interfaz imponga una restricción porque alguien podría querer romperla. Ese no es un buen argumento.
philofinfinitejest
77
@EricLippert En el caso en el que se especifica el parámetro opcional para conveniencia de codificación, sí, imponer inconvenientes en la implementación es contra-intuitivo. Pero en el caso de que el parámetro opcional se esté utilizando para minimizar la sobrecarga del método, una característica poderosa, esos valores opcionales pueden tener una gran importancia e ignorarlos ciertamente diluye esa potencia. Sin mencionar el caso donde la implementación concreta especifica un valor predeterminado diferente al de la interfaz. Simplemente parece una desconexión muy extraña permitir.
philofinfinitejest
8
Estoy de acuerdo con @philofinfinitejest aquí. Una interfaz dice qué puede hacer algo. Si se me pasa una implementación de una interfaz con un valor predeterminado diferente al especificado por la interfaz, ¿cómo voy a saber eso? Oye, tengo una interfaz que pasa true como predeterminada, entonces, ¿por qué esto se está volviendo falso? Eso parecería que NO obtuve la interfaz que esperaba. Peor aún, ahora tengo que programar una implementación y no una interfaz.
aaronburro
47

Un parámetro opcional se etiqueta con un atributo. Este atributo le dice al compilador que inserte el valor predeterminado para ese parámetro en el sitio de la llamada.

La llamada obj2.TestMethod();se reemplaza por obj2.TestMethod(false);cuando el código C # se compila a IL, y no en el momento JIT.

Entonces, en cierto modo, siempre es la persona que llama proporcionando el valor predeterminado con parámetros opcionales. Esto también tiene consecuencias en el control de versiones binarias: si cambia el valor predeterminado pero no vuelve a compilar el código de llamada, continuará utilizando el valor predeterminado anterior.

Por otro lado, esta desconexión significa que no siempre puedes usar la clase concreta y la interfaz de manera intercambiable.

Ya no puede hacerlo si el método de interfaz se implementó explícitamente .

CodesInChaos
fuente
1
¿Puede forzar a los implementadores a implementar explícitamente?
aplastar
30

Debido a que los parámetros predeterminados se resuelven en tiempo de compilación, no en tiempo de ejecución. Por lo tanto, los valores predeterminados no pertenecen al objeto al que se llama, sino al tipo de referencia por el que se llama.

Olhovsky
fuente
7

Los parámetros opcionales son como una macro sustitución de lo que entiendo. No son realmente opcionales desde el punto de vista del método. Un artefacto de eso es el comportamiento que ve cuando obtiene resultados diferentes si lanza a una interfaz.

Ariel Arjona
fuente