c# - ultima - Buscar llamadas a métodos asíncronos no esperados
llamadas entrantes no aparecen moto g (4)
Acabo de tropezar con un escenario bastante peligroso al migrar una aplicación ASP.NET al modelo async / await.
La situación es que hice un método asincrónico: la async Task DoWhateverAsync()
, cambié la declaración en la interfaz a la Task DoWhateverAsync()
y esperaba que el compilador me dijera dónde está el código ahora incorrecto, a través de esa advertencia . Bueno, mala suerte. Siempre que ese objeto se inyecta a través de la interfaz, la advertencia no ocurre. :-(
Esto es peligroso. ¿Hay alguna manera de verificar automáticamente los métodos no esperados que devuelven tareas? No me importan unas pocas advertencias, pero no me gustaría perder una.
Aquí hay un ejemplo:
using System.Threading.Tasks;
namespace AsyncAwaitGames
{
// In my real case, that method just returns Task.
public interface ICallee { Task<int> DoSomethingAsync(); }
public class Callee: ICallee
{
public async Task<int> DoSomethingAsync() => await Task.FromResult(0);
}
public class Caller
{
public void DoCall()
{
ICallee xxx = new Callee();
// In my real case, the method just returns Task,
// so there is no type mismatch when assigning a result
// either.
xxx.DoSomethingAsync(); // This is where I had hoped for a warning.
}
}
}
El compilador emitirá una advertencia CS4014 pero solo se emitirá si el método de llamada es async
.
Sin advertencia:
Task CallingMethod() {
DoWhateverAsync();
// More code that eventually returns a task.
}
Advertencia CS4014: Debido a que no se espera esta llamada, la ejecución del método actual continúa antes de que se complete la llamada. Considere la posibilidad de aplicar el operador ''esperar'' al resultado de la llamada.
async Task CallingMethod() {
DoWhateverAsync();
}
Esto no es terriblemente útil en su caso específico porque tiene que encontrar todos los lugares donde se llama DoWhateverAsync
y cambiarlos para obtener la advertencia y luego corregir el código. Pero quería usar la advertencia del compilador para encontrar estas llamadas en primer lugar.
Sugiero que use Visual Studio para encontrar todos los usos de DoWhateverAsync
. Deberá modificar el código circundante de todos modos ya sea mediante advertencias del compilador o mediante una lista de usos.
Al final, usamos roslyn para encontrar todas las instancias donde se ignoró un valor de retorno de Tarea o Tarea <>:
if (methodSymbol.ReturnType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
// For all such symbols, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());
syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
if (((INamedTypeSymbol) methodSymbol.ReturnType).IsGenericType && ((INamedTypeSymbol) methodSymbol.ReturnType).BaseType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
// For all such symbols, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());
syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
Puede agregar una advertencia específica en las propiedades del Proyecto VS como una que arroje un error de compilación, como se describe aquí
Puede agregar una lista de códigos de advertencia separados por CS4014
y CS4014
, por ejemplo CS4014
, y el compilador no podrá compilar si no está esperando un método async
.
Aquí hay una captura de pantalla con VS2017: configuración VS2017 para arrojar errores del compilador para advertir CS4014
Tienes pocas opciones:
- Esta es la solución más simple de "Cavernícola" use la búsqueda incorporada en funcionalidad de búsqueda VS (CTRL + MAYÚS + F) en Toda la solución, también en Opciones de búsqueda, haga clic en la casilla Usar expresión regular y use esta expresión regular:
(?<!await|task(.*))/s([_a-zA-Z0-9/.])*Async/(
Asume que publique todo su método async fijo con la palabra clave Async y la llamada al método está en una línea . Si no lo está verdadero, entonces no lo use (o agregue las validaciones faltantes a la expresión). - Utilice alguna herramienta de analizador de código de terceros, el paquete Nuget. ReSharper es muy popular y creo que puede detectar este problema o puede crear sus propias reglas.
- Mi elección sería usar Roslyn ( @Volker proporcionó una solución ). Puede crear su propio conjunto de reglas con soluciones de corrección de código (el ícono de la bombilla mostrará la corrección de su código) por lo que este es el mejor.
Cómo usar Roslyn:
- Tienes que instalar .NET Compiler Platform SDK: de aquí
- Utilice VS 2017 versión 15.2 (o superior)
- Cree un nuevo proyecto Archivo -> Nuevo -> Proyecto, en el grupo Extensibilidad, seleccione: Analizador con corrección de código (Nuget + VSIX) . Tiene que apuntar a .NET Framework 4.6.2 para crear este proyecto.
Puede copiar pegar la solución anterior. Crear
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncAwaitAnalyzer : DiagnosticAnalyzer
{ ...
}
clase con lógica, para detectar el problema. Y crea
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AsyncAwaitCodeFixProvider)), Shared]
public class AsyncAwaitCodeFixProvider : CodeFixProvider
{ ...
}
clase para proporcionar sugerencias de reparación (agregar espera) al problema.
Después de una compilación exitosa obtendrá su propio paquete .wsix, puede instalarlo en su instancia de VS y después de un reinicio de VS debe comenzar a detectar los problemas.