Considere el siguiente código:
using System; static class Program { public static void Main() { Test test = new Test(); test.Method(()=>{}); } } public sealed class Test { public void Method(Delegate _) { Console.WriteLine("Test.Method(Delegate)"); } } public static class TestExt { public static void Method(this Test self, Action _) { Console.WriteLine("TestExt.Method(Action)"); } } En C# 9.0 dirigido a net48 , esto genera TestExt.Method(Action)
En C# 10.0 dirigido a net6.0 , esto genera Test.Method(Delegate)
Obviamente, este es un cambio importante porque el método de extensión puede ejecutar un código diferente.
Mi pregunta es: ¿hay alguna manera de detectar este tipo de cambio importante en el código sin inspeccionar todo el código? Por ejemplo, ¿hay alguna regla de análisis de código que podamos habilitar para detectar este tipo de código?
Este cambio en realidad ha causado un error genuino en nuestra base de código.
Tenga en cuenta que esta diferencia de comportamiento solo se produce debido a cambios en la resolución del método de extensión. Si en lugar de un método de extensión, el método está en la propia clase Test , entonces ambas versiones de lenguaje llaman a Test.Method(Action) :
static class Program { public static void Main() { Test test = new Test(); test.Method(()=>{}); } } public sealed class Test { public void Method(Delegate _) { Console.WriteLine("Test.Method(Delegate)"); // Not called by either version. } public void Method(Action _) { Console.WriteLine("TestExt.Method(Action)"); // Called by C#9 and C#10. } }Información de contexto:
Este cambio radical nos ha mordido porque tenemos (con razón o sin ella) un método de extensión para Control.BeginInvoke() del siguiente modo (se omite el manejo de errores):
public static void BeginInvoke(this Control control, Action action) { if (control.IsHandleCreated) tryBeginInvoke(control, action); }Esto ya no se llama para código como:
this.BeginInvoke(() => someMenu.Visible = somethingIsAvailable()); En su lugar, se llama a Control.BeginInvoke(Delegate) , que omite la verificación de control.IsHandleCreated y el método de manejo de errores tryBeginInvoke() .