简体   繁体   中英

C# Noob - Passing a condition as a parameter

I already have a solution for this problem, but it seems very confusing and unpractical to me.

What I intend to do is to read a file line-by-line (due to the file size, it's unpractical to load it into memory all at once), and if certain condition is met (eg: the line matches a regex pattern, or contains certain keywords, or is equal to certain string)

Here's what I'd Ideally have:

 void TryGetLineIf(string filePath, Condition condition, out string desiredLine)
  {
     StreamReader fileReader = new StreamReader(filePath);
     string currentLine = "";

     while (!fileReader.EndOfStream)
     {
        currentLine = fileReader.ReadLine();

        if (condition)
        {
           desiredLine = currentLine;
        }
     }
  }

However, I don't know what to do with the condition parameter. The only way out I can think of is to replace the condition with an enum, (LineSelectionOptions.IsRegexMatch, LineSelectionOptions.ContainsString ...), add an extra parameter to the void and switch between possible values for it. I'm working with .NET 2.0, if that is relevant.

If you know the parameters your function will have, you could use Func to pass a function which will return a bool

your method definition will be like this

 void TryGetLineIf(string filePath, Func<string, bool> condition, out string desiredLine)

The if-line will be like this

if(condition(currentLine)) {
   desiredLine = currentLine;
}

and the call to the method will be something like this

Func<string, bool> condition = (line) => line.Length > 1;
string desiredLine;
TryGetLineIf("C:\\....file.pdf", condition, out desiredLine)

BUT since you're working in 2.0, you might want to simulate Func using delegates See this How to simulate a "Func<(Of <(TResult>)>) Delegate" in .NET Framework 2.0? or this Replacing Func with delegates C#

Please excuse the formatting because I am on a mobile phone.

Anyhow, I think this is a perfect candidate for using yield return because the caller can decide to break and not want to read the rest of the file after any line. This will allow that. Plus the caller can perform chaining and do further processing. Furthermore, no need for an out parameter.

Make sure to use the using statement and if available in .NET 2.0, use the File.ReadLines method instead: it reads line by line and it's much simpler. I will try and edit my answer with the suggestions I have made once I get home.

public static IEnumerable<string> TryGetLineIf(string filePath, Condition condition
{
     StreamReader fileReader = new StreamReader(filePath);
     string currentLine = "";

 while (!fileReader.EndOfStream)
 {
    currentLine = fileReader.ReadLine();

    if (condition.MeetsCriteria(currentLine))
    {
         yield  return currentLine;
    }

    }
} 

And as a final note it is more robust if you pass a delegate as the condition instead of a class instance as suggested in another answer.

@Gonzalo.- Thanks for the answer, I managed to use it in a few cases. But now I have a problem when attempting to do that with more parameters in the Func delegate, eg, when trying to check if the line matches a certain Regex pattern:

Func<string, string, bool> condition = 
(line, pattern) => new Regex(pattern).IsMatch(line)

IEnumerable<string> LinesIWant =
ReturnLineIf("C:\test.txt", condition);

And here's the method:

IEnumerable<string> ReturnLineIf(string filePath, Func<string, string, bool> condition)
{
  // (snip)
  // How do I specify a pattern when calling the method?
  if (condition(currentLine, "This should be the pattern..."))
  {
    yield return currentLine;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM