c# - parametros - Está reemplazando un parámetro opcional con sobrecargas un cambio de rotura?
optional parameters must specify a default value (2)
Soy consciente de que agregar un parámetro opcional en un método de biblioteca es un cambio radical ,
void Foo(int x) // OLD
void Foo(int x, int y = 5) // NEW
porque en el código compilado, la nueva versión se ve como Foo(int, int)
. Cada llamada de Foo(0)
(código fuente) se traduce a Foo(0, 5)
(código compilado) por el compilador. Por lo tanto, un cliente antiguo que utiliza una llamada compilada de Foo(0)
no encontraría un método adecuado.
¿Qué pasa con la otra dirección?
void Foo(int x, int y = 5) { ... } // OLD
void Foo(int x) { Foo(x, 5); } // NEW
void Foo(int x, int y) { ... } // NEW
Foo(0)
(código fuente) aún compilaría, y Foo(0, 5)
(código compilado) aún encontraría una sobrecarga adecuada, por lo que, teóricamente, esto debería funcionar.
¿Funciona en la práctica, es decir, este escenario es "oficialmente soportado" por el tiempo de ejecución .NET y los compiladores C # / VB? ¿O de alguna manera las llamadas a métodos con parámetros opcionales están "marcadas", haciendo que fallen cuando los parámetros opcionales son reemplazados por sobrecargas?
EDITAR: Para aclarar, pregunto acerca de la compatibilidad binaria : ¿Es posible reemplazar library.dll (old)
con library.dll (new)
sin recompilar projectUsingLibrary.exe
?
No estoy seguro de si mi método de prueba fue el mejor, pero esto es lo que descubrí a partir de: (disculpas por los nombres de clase y nombre de espacio)
namespace ClassLibrary1
{
public class Class1
{
private int x;
private int y;
public void Foo(int x)
{
Foo(x, 0);
}
public void Foo(int x, int y = 5)
{
this.x = x;
this.y = y;
}
}
}
Construí esto y agregué el dll a una aplicación de consola en una solución diferente y hice referencia al archivo dll al navegar hacia él:
using ClassLibrary1;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var c = new Class1();
c.Foo(1);
c.Foo(2, 3);
c.Foo(3, 5);
}
}
}
Luego cambié las firmas de métodos de la biblioteca de clases a:
namespace ClassLibrary1
{
public class Class1
{
private int x;
private int y;
public void Foo(int x)
{
Foo(x, 0);
}
public void Foo(int x, int y)
{
this.x = x;
this.y = y;
}
}
}
Luego compilé la biblioteca de clases y copié el dll en la carpeta de aplicaciones de la consola y ejecuté la aplicación de la consola; no hubo problemas para cambiar la firma, pero como dije, no estoy seguro de si mi método de prueba es suficiente.
Entonces, para responder a su pregunta, puede cambiar la biblioteca de la manera que ha especificado sin tener que volver a compilar su ejecutable.
Pensé que era una buena pregunta, así que aquí va mi opinión.
Usando un cliente rápido que hace esto:
c1.Foo(1);
c1.Foo(1, 2);
Cuando se usa un parámetro opcional, el cliente IL se ve así:
IL_0000: nop
IL_0001: newobj instance void [ClassLibrary1]ClassLibrary1.Class1::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: ldc.i4.5
IL_000a: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_000f: nop
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: ldc.i4.2
IL_0013: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32)
IL_0018: nop
IL_0019: ret
y cuando se usan sobrecargas, se ve así:
IL_0000: nop
IL_0001: newobj instance void [ClassLibrary2]ClassLibrary2.Class2::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32)
IL_000e: nop
IL_000f: ldloc.0
IL_0010: ldc.i4.1
IL_0011: ldc.i4.2
IL_0012: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32, int32)
IL_0017: nop
IL_0018: ret
Por lo tanto, si cambió la implementación de opcional a sobrecarga, pero dejó el cliente como estaba originalmente, estaría efectivamente agregando el parámetro predeterminado para usted, y siempre llamando a la función que tiene dos argumentos, que puede ser o no el comportamiento deseado