使用正则表达式查找缺少“Async”后缀的C#异步方法?

3

C#中,异步方法(被标记为async关键字的方法)的一个约定是它们的名称应以“Async”作为后缀:

public async Task MyMethod(/* params */) ...         <-- Bad, missing "Async" suffix
public async Task MyMethodAsync(/* params */) ...    <-- Good, as per convention

我最近写了很多异步代码,我担心我可能忘记在一些方法上加上“Async”后缀。

是否有任何正则表达式可以用来查找那些被标记为async但是没有“Async”后缀的方法?如果可能的话,反过来也行(即查找已经有“Async”后缀但是没有标记async关键字的方法)?

如果有可能的话,我会将正则表达式复制到Visual Studio中的“查找文件”对话框中,如果这对答案有所影响的话。

我猜像FxCop、CodeRush或ReSharper等工具可能已经提醒了这样的问题,然而由于我的工作场所,后两者不是一个选项,如果我可以使用一个简单的正则表达式来完成这个任务,我宁愿不安装和配置前者。


6
您可以使用反射编写单元测试来检查这一点。 - usr
3
你对正则表达式了解多少?你是否真的尝试过,还是仅仅是一个“求代码”的请求? - Magnus Hoff
就此而言,ReSharper没有警告。事实上,在输入方法时,它会自动建议以“Task”结尾。 - James Thorpe
啊,又被Smurf命名规则搞糊涂了... - David Arno
使用像FxCop这样的工具(我不知道它是否默认执行此操作)的好处在于它也会捕获未来的问题。正则表达式可能会解决您今天的问题,但不能防止您再次犯同样的错误。 - John Koerner
显示剩余4条评论
3个回答

9

使用正则表达式:

(?i)async.*(?<!async)\(

它查找关键字async,然后在没有(“?<!”) async之前的“(”处进行匹配。

编辑: 更好的版本。感谢@Andrew

Task\s+(\w+(?<!Async))\(

这将为您提供带有小的异步方法的方法。此外,它允许使用查找和替换功能。

1
不错!我会将它改为@"Task\s+(\w+(?<!Async))\(",不区分大小写。这还可以捕获方法名称,因此您可以替换附加和替换"Async"。 - Andrei Tătar
你添加的“Task”无法找到async void方法。而在开头加上(?i)可以忽略大小写。但是捕获方法以便使用查找和替换功能非常方便。 - LzyPanda
@LzyPanda 谢谢你的回复。显然,这被称为“零宽度负向后顾断言”。在使用正则表达式时,我以前从未使用过这种方法。 - Steven Rands
2
@StevenRands 请尝试使用 Task(?:<?\w*>?)\s+(\w+(?<!Async))\( - LzyPanda
1
@LzyPanda,与其他所有东西相比,你的正则表达式无疑是最快速和最准确的。 - Korayem
显示剩余3条评论

5

以下是一个同时适用于泛型方法和返回结果的方法(可能再次使用泛型)。

 async (void|Task(<.*>)?) [^<\(]*(?<!Async)(?:<.*?>)?\(

我已经测试了以下的模式:

 async void Test()
 async void Test<T>()
 async void Test<T, U>()
 async Task Test()
 async Task Test<T>()
 async Task Test<T, U>()
 async Task<T> Test()
 async Task<T> Test<T>()
 async Task<T> Test<T, U>()
 async Task<T<U>> Test()
 async Task<T<U>> Test<U>()
 async Task<T<U>> Test<T, U>()

这个正则表达式错误地匹配了 public async Task<IEnumerable<Entity>> GetMembersByIdsAsync() - Korayem
@Korayem:你用的是哪个正则表达式引擎?我无法确认这一点。该表达式在末尾没有“Async”的情况下可以正确匹配,但在末尾加上“Async”则无法匹配。这是应该的工作方式。请参见此链接:https://regex101.com/r/RF7FBL/2 - dnickless
在启用正则表达式的情况下,在 Visual Studio 中查找文件。 - Korayem
@Korayem:我在Visual Studio 2017中尝试了你的示例,我的建议的正则表达式也无法匹配(这是正确的)。我认为你的观察并不正确。 - dnickless
哦,我意外地得到了与@Korayem相同的结果,它可以正确匹配以Async结尾的名称方法... 我在这里使用的是VS2019,Match Case和Use Regular Expressions都开启了... 即使将负向先行断言更改为更简单的匹配方式,在VS中也会出现错误匹配... 奇怪! ((async )?Task(<.*>)?|async (void|Task(<.*>)?)) [^<\(]*([^Async])(?:<.*?>)?\(在.NET Regex测试器上尝试了两个版本,都表现如预期,可能是VS的一个bug?! - benmccallum
显示剩余3条评论

4

干得好,清晰的代码胜利! :) 除了其他答案之外,我会通过使用 grep 工具来实现:

grep '\basync\b' file.cs | grep -v 'Async('

第一个grep查找所有包含单词async的行,第二个grep过滤出不包含Async(的行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接