Выражение C #, эквивалентное блочному коду ruby's - PullRequest
2 голосов
/ 18 июня 2011

Я разработчик .NET и недавно начал изучать ruby ​​с ruby_koans. Некоторые синтаксисы Ruby поразительны, и один из них - способ обработки кода «сэндвич».

Ниже приведен код сэндвича с рубином.

  def file_sandwich(file_name)
    file = open(file_name)
    yield(file)
  ensure
    file.close if file
  end

  def count_lines2(file_name)
    file_sandwich(file_name) do |file|
      count = 0
      while line = file.gets
        count += 1
      end
      count
    end
  end

  def test_counting_lines2
    assert_equal 4, count_lines2("example_file.txt")
  end

Я восхищен тем, что могу избавиться от громоздкого «кода открытия и закрытия файла» каждый раз, когда я получаю доступ к файлу, но не могу придумать никакого кода, эквивалентного C #. Может быть, я могу использовать динамический прокси IoC, чтобы сделать то же самое, но есть ли способ сделать это чисто с помощью C #?

Большое спасибо заранее.

Ответы [ 3 ]

8 голосов
/ 18 июня 2011

Вам, конечно, здесь не нужно ничего связанного с IoC. Как насчет:

public T ActOnFile<T>(string filename, Func<Stream, T> func)
{
    using (Stream stream = File.OpenRead(stream))
    {
        return func(stream);
    }
}

public int CountLines(string filename)
{
    return ActOnFile(filename, stream =>
    {
        using (StreamReader reader = new StreamReader(stream))
        {
            int count = 0;
            while (reader.ReadLine() != null)
            {
                count++;
            }
            return count;
        }
    });
}

В этом случае это не очень помогает, поскольку оператор using уже делает большую часть того, что вы хотите ... но общий принцип верен. Действительно, вот как LINQ настолько гибок. Если вы еще не смотрели на LINQ, я настоятельно рекомендую вам это сделать.

Вот метод, который я бы использовал act CountLines:

public int CountLines(string filename)
{
    return File.ReadLines(filename).Count();
}

Обратите внимание, что это все равно будет считывать только строку за раз ... но метод расширения Count действует на возвращаемую последовательность.

В .NET 3.5 это будет:

public int CountLines(string filename)
{
    using (var reader = File.OpenText(filename))
    {
        int count = 0;
        while (reader.ReadLine() != null)
        {
            count++;
        }
        return count;
    }
}

... все еще довольно просто.

3 голосов
/ 18 июня 2011

Вы просто ищете что-то, что открывает и закрывает поток для вас?

public IEnumerable<string>GetFileLines(string path)
{
    //the using() statement will open, close, and dispose your stream for you:
    using(FileStream fs = new FileStream(path, FileMode.Open))
    {
       //do stuff here
    }
}
0 голосов
/ 18 июня 2011

Является ли yield return тем, что вы ищете?

using вызовет Dispose () и Close (), когда он достигнет закрывающей скобки, но я думаю, что вопрос заключается в том, как добиться этой конкретной структуры кода.

Редактировать: Просто понял, что это не совсем то, что вы ищете, но я оставлю этот ответ здесь, так как многие люди не знают об этой технике.

static IEnumerable<string> GetLines(string filename)
{
    using (var r = new StreamReader(filename))
    {
        string line;
        while ((line = r.ReadLine()) != null)
            yield return line;
    }
}

static void Main(string[] args)
{
    Console.WriteLine(GetLines("file.txt").Count());

    //Or, similarly: 

    int count = 0;
    foreach (var l in GetLines("file.txt"))
        count++;
    Console.WriteLine(count);
}
...