I have a method in a C# program. It enumerates all the .cs files in a certain folder and then runs through the list. For each file, I read all the lines using File.ReadAllLines on it. I only want to process a file if it contains a class, whether conventional, static, or abstract, whose name begins with a certain phrase and does not end with the word Tests. Moreover, I wish to find the line index in the line of lines containing the declaration of the class --- i.e., the public static class Foo part.
Given that I take the result of File.ReadAllLines and call ToList() on it to create a List<string>, I wish to use the FindIndex method to find the index of the line matching my criteria (if it exists) using a Predicate.
My question is: What is a good way to write such a predicate?
I realize I could probably use more sophisticated methods, but I am just putting this code into a quick-and-dirty LINQPad script. So, I don't have to get super fancy.
Let me show you what I have so far (assume that the outermost namespace and class are already suitably declared):
void Main()
{
var files = Directory
.EnumerateDirectories(
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile
), @"source\repos\astrohart\MFR"
), "*", SearchOption.TopDirectoryOnly
).SelectMany(
x => Directory.EnumerateFiles(
x, "FileSystemEntry*.cs", SearchOption.AllDirectories
)
)
.Where(x => !"FileSystemEntry.cs".Equals(Path.GetFileName(x)))
.ToList();
if (files == null || !files.Any()) return;
foreach (var file in files)
{
var contents = string.Empty;
try
{
contents = File.ReadAllText(file);
}
catch (Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
contents = string.Empty;
}
if (string.IsNullOrWhiteSpace(contents)) continue;
if (contents.Contains("[TestFixture]")) continue;
if (contents.Contains("[Log(AttributeExclude = true)]")) continue;
file.Dump();
var lines = new List<string>();
lines.TrimExcess();
try
{
lines = File.ReadAllLines(file).ToList();
}
catch (Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
lines = new List<string>();
lines.TrimExcess();
}
if (lines == null || !lines.Any()) continue;
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;
/*...*/
}
}
How do I translate the for loop in:
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;
into a call thus:
var index = lines.FindIndex(currentLine => /*...*/);
I need help with how to derive the proper body of the lambda expression that matches what the for loop does.
Thanks in advance!
EDIT 1
I squinted my eyes at my loop just a little more. I am looking for a predicate to use specifically with the FindIndex method. I thought a little harder and I figured out maybe I can get away with:
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"));
Perhaps I can implement an extension method
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;
}
Then I'd declare another method:
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"
);
}
Then use it thus:
var index = lines.FindIndex(KeepLine);
Would that work?