如何将C#中的for循环转换为FindIndex的Predicate表达式?
问题:将类声明行查找逻辑改写为FindIndex+Predicate形式
我在C#程序里写了个方法,会枚举指定文件夹下所有.cs文件并遍历。对每个文件,用File.ReadAllLines读取所有行,只处理那些包含名称以特定短语开头且不以Tests结尾的常规、static或abstract类的文件,同时要定位到类声明行(比如public static class Foo)的索引。
我已经把File.ReadAllLines的结果转成了List<string>,想把现有的for循环逻辑改成用lines.FindIndex(currentLine => /*...*/)的形式,自己试了两种写法,想问问这些方案是否可行。
现有循环实现代码
var index = -1; for (var i = 0; i < lines.Count; i++) { var currentLine = lines[i].Trim(); if (currentLine.EndsWith("Tests")) continue; if (currentLine.StartsWith("public static class FileSystemEntry")) { index = i; break; } if (currentLine.StartsWith("public class FileSystemEntry")) { index = i; break; } if (currentLine.StartsWith("public abstract class FileSystemEntry")) { index = i; break; } } if (index < 0) continue;
我的两种尝试写法
尝试1:直接Lambda写法
var index = lines.FindIndex(currentLine => !currentLine.Trim().EndsWith("Tests") && currentLine.Trim().StartsWith("public static class FileSystemEntry") || currentLine.Trim().StartsWith("public class FileSystemEntry") || currentLine.Trim().StartsWith("public abstract class FileSystemEntry"));
尝试2:扩展方法+判断方法写法
扩展方法定义
public static bool StartsWithAnyOf(this string value, params string[] testStrings) { var result = false; try { if (string.IsNullOrWhiteSpace(value.Trim())) return result; if (testStrings == null || !testStrings.Any()) return result; foreach(var element in testStrings) if (value.Trim().StartsWith(element)) { result = true; break; } } catch { result = false; } return result; }
判断方法定义
public static bool KeepLine(string currentLine) { if (string.IsNullOrWhiteSpace(currentLine.Trim())) return false; if (currentLine.Trim().EndsWith("Tests")) return false; return currentLine.StartsWithAnyOf( "public static class FileSystemEntry", "public class FileSystemEntry", "public abstract FileSystemEntry" ); }
调用方式
var index = lines.FindIndex(KeepLine);
方案可行性分析与优化建议
尝试1:直接Lambda写法
可行,但需修正两个问题:
- 逻辑运算符优先级错误:
&&优先级高于||,原写法会被解析为「(!Tests结尾 && 静态类) || 常规类 || 抽象类」,和原循环逻辑(先排除Tests结尾的行,再匹配类声明)不符。需给||部分加括号修正:var index = lines.FindIndex(currentLine => !currentLine.Trim().EndsWith("Tests") && (currentLine.Trim().StartsWith("public static class FileSystemEntry") || currentLine.Trim().StartsWith("public class FileSystemEntry") || currentLine.Trim().StartsWith("public abstract class FileSystemEntry"))); - 重复调用
Trim():每一行多次调用Trim()浪费性能,可提前缓存修剪后的结果。
尝试2:扩展方法+判断方法写法
整体可行,但有细节优化空间:
- 避免重复
Trim():扩展方法中多次调用value.Trim(),建议缓存结果:var trimmedValue = value.Trim(); - 移除冗余try-catch:
Trim()和StartsWith()正常场景不会抛异常,只需提前判断value是否为null即可 - 修正参数笔误:
KeepLine方法里第三个前缀少了class,应改为"public abstract class FileSystemEntry"
优化后的扩展方法:
public static bool StartsWithAnyOf(this string value, params string[] testStrings) { if (string.IsNullOrWhiteSpace(value)) return false; var trimmedValue = value.Trim(); if (string.IsNullOrWhiteSpace(trimmedValue)) return false; if (testStrings == null || testStrings.Length == 0) return false; foreach (var element in testStrings) { if (trimmedValue.StartsWith(element)) { return true; } } return false; }
更简洁的替代写法
可以把类声明前缀提取成数组,结合LINQ的Any()方法,既避免重复操作,又让逻辑更清晰:
var classPrefixes = new[] { "public static class FileSystemEntry", "public class FileSystemEntry", "public abstract class FileSystemEntry" }; var index = lines.FindIndex(line => { var trimmedLine = line.Trim(); return !trimmedLine.EndsWith("Tests") && classPrefixes.Any(prefix => trimmedLine.StartsWith(prefix)); });
内容的提问来源于stack exchange,提问作者Dr. Brian Hart
相关产品推荐
相关产品推荐

