2010-04-02 29 views
55

Uso de C# 4.0: creación de una interfaz y una clase que implementa la interfaz. Quiero declarar un parámetro opcional en la interfaz y hacer que se refleje en la clase. Entonces, tengo lo siguiente:Parámetros opcionales para las interfaces

public interface IFoo 
{ 
     void Bar(int i, int j=0); 
} 

public class Foo 
{ 
     void Bar(int i, int j=0) { // do stuff } 
} 

Esto compila, pero no se ve bien. La interfaz debe tener los parámetros opcionales, porque de lo contrario no se refleja correctamente en la firma del método de interfaz.

¿Debo omitir el parámetro opcional y simplemente usar un tipo que admite nulos? ¿O funcionará como está previsto sin efectos secundarios o consecuencias?

+2

Los comentarios de Martin a continuación dan un ejemplo detallado de las diversas dificultades, y me gustaría que el compilador marque los argumentos por defecto que no coincidan. Dicho esto, estoy usando esto en mi código porque indica mi intención como desarrollador tanto en la interfaz como en el nivel de implementación para que otros desarrolladores lo puedan ver. –

+0

Preguntas interesantes relacionadas, "[¿Hay alguna razón para declarar parámetros opcionales en una interfaz?] (Https: // stackoverflow.com/questions/6752762 /) ", y," [¿Por qué los parámetros opcionales C# 4 definidos en la interfaz no se aplican en la implementación de clase?] (https://stackoverflow.com/questions/4922714/) ", el segundo con un interesante [respuesta de Lippert] (https://stackoverflow.com/a/4923642/1028230). – ruffin

Respuesta

28

usted podría considerar la alternativa pre-parámetros_opcionales:

public interface IFoo 
{ 
    void Bar(int i, int j); 
} 

public static class FooOptionalExtensions 
{ 
    public static void Bar(this IFoo foo, int i) 
    { 
     foo.Bar(i, 0); 
    } 
} 

Si no te gusta el aspecto de una nueva característica del lenguaje, que no tiene que usarlo.

+40

Pero ... ¡es nuevo! ¡Y brillante! :-) – bryanjonker

+1

No dude en dar una explicación adicional de cómo funciona esto, o quizás un enlace a lo que se refiere como "alternativa pre-opcional de parámetros". ¡Podría ayudar a los futuros usuarios!:] –

+0

Por supuesto, la forma más antigua de hacerlo (antes de la introducción de los métodos de extensión de C# 3) era utilizar métodos de sobrecarga en ambos la clase y la interfaz. Si tiene acceso al código de clase, la sobrecarga probablemente sea mejor que un método de extensión, ya que mantiene el código en un solo lugar. –

1

Mira a un lado, eso hará exactamente lo que parece que quiere lograr.

+2

Esto no proporciona una respuesta a la pregunta. Criticar o solicitar aclaraciones de un autor , deja un comme nt debajo de su publicación. –

+3

No lo sé. Me parece que es una respuesta directa a la pregunta: "¿O esto funcionará como está previsto sin efectos secundarios o consecuencias?" – MojoFilter

3

¿Qué tal algo así?

public interface IFoo 
{ 
    void Bar(int i, int j); 
} 

public static class IFooExtensions 
{ 
    public static void Baz(this IFoo foo, int i, int j = 0) 
    { 
     foo.Bar(i, j); 
    } 
} 

public class Foo 
{ 
    void Bar(int i, int j) { /* do stuff */ } 
} 
+0

@Iznogood - la edición que aprobó aquí claramente no es una edición válida: http://stackoverflow.com/review/suggested-edits/1041521. Tenga más cuidado al revisar las ediciones. – LittleBobbyTables

44

Lo que es realmente extraño es que el valor se pone para el parámetro opcional en la interfaz de realidad hace la diferencia. Supongo que debe preguntarse si el valor es un detalle de interfaz o un detalle de implementación. Hubiera dicho esto último, pero las cosas se comportan como la primera. El siguiente código muestra 1 0 2 5 3 7 por ejemplo.

// Output: 
// 1 0 
// 2 5 
// 3 7 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    interface IMyOtherTest 
    { 
     void MyTestMethod(int notOptional, int optional = 7); 
    } 

    class MyTest : IMyTest, IMyOtherTest 
    { 
     public void MyTestMethod(int notOptional, int optional = 0) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      myTest1.MyTestMethod(1); 

      IMyTest myTest2 = myTest1; 
      myTest2.MyTestMethod(2); 

      IMyOtherTest myTest3 = myTest1; 
      myTest3.MyTestMethod(3); 
     } 
    } 
} 

Lo que es bastante interesante es que si su interfaz hace que un parámetro opcional de la clase que implementa que no tiene que hacer lo mismo:

// Optput: 
// 2 5 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    class MyTest : IMyTest 
    { 
     public void MyTestMethod(int notOptional, int optional) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      // The following line won't compile as it does not pass a required 
      // parameter. 
      //myTest1.MyTestMethod(1); 

      IMyTest myTest2 = myTest1; 
      myTest2.MyTestMethod(2); 
     } 
    } 
} 

Lo que parece ser un error, sin embargo, es que si Implementa la interfaz explícitamente, el valor que das en la clase para el valor opcional no tiene sentido. ¿Cómo en el siguiente ejemplo podrías usar el valor 9? Esto ni siquiera da una advertencia al compilar.

// Optput: 
// 2 5 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    class MyTest : IMyTest 
    { 
     void IMyTest.MyTestMethod(int notOptional, int optional = 9) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      // The following line won't compile as MyTest method is not available 
      // without first casting to IMyTest 
      //myTest1.MyTestMethod(1); 

      IMyTest myTest2 = new MyTest();    
      myTest2.MyTestMethod(2); 
     } 
    } 
} 
2

No es necesario que el parámetro sea opcional en la implementación. Su código tendrá más sentido que:

public interface IFoo 
{ 
     void Bar(int i, int j = 0); 
} 

public class Foo 
{ 
     void Bar(int i, int j) { // do stuff } 
} 

De esta manera, no es ambiguo cuál es el valor predeterminado. De hecho, estoy bastante seguro de que el valor predeterminado en la implementación no tendrá ningún efecto, ya que la interfaz proporciona un valor predeterminado para él.

+2

El valor predeterminado en la implementación tendrá un efecto si su referencia se escribe con la clase en lugar de la interfaz. –

2

Lo que hay que tener en cuenta es lo que sucede cuando se utilizan marcos de Mocking, que funcionan en función del reflejo de la interfaz. Si se definen parámetros opcionales en la interfaz, el valor predeterminado se aprobará según lo declarado en la interfaz. Un problema es que no hay nada que te impida establecer diferentes valores opcionales en la definición.

Cuestiones relacionadas