2015-11-11 10 views
0

У меня есть тестовый код, который готовит MemoryStream, который в конечном итоге будет считаться объектом. Вот как я хочу, чтобы писать:Почему StreamWriter должен быть открыт для доступа к моему MemoryStream?

 var manager = new LeaderboardImportManager(leaderboard); 
     var columnNames = manager.ColumnNames; 

     var stream = new MemoryStream(); 
     using (var writer = new StreamWriter(stream)) 
     { 
      writer.WriteLine(string.Join(",", columnNames)); 
      foreach (var user in users) 
      { 
       var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList(); 
       row.Insert(0, user.UserName); 
       writer.WriteLine(string.Join(",", row)); 
      } 

      writer.Flush(); 
      stream.Position = 0; 
     } 

     return stream; 

Но когда я делаю это таким образом, мой объект поток становится нечитаемым, и мой тест не пройден, так что я должен сделать это следующим образом:

 var manager = new LeaderboardImportManager(leaderboard); 
     var columnNames = manager.ColumnNames; 

     var stream = new MemoryStream(); 
     var writer = new StreamWriter(stream); 

     writer.WriteLine(string.Join(",", columnNames)); 
     foreach (var user in users) 
     { 
      var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList(); 
      row.Insert(0, user.UserName); 
      writer.WriteLine(string.Join(",", row)); 
     } 

     writer.Flush(); 
     stream.Position = 0; 
     return stream; 

Это, конечно, мешает мне избавиться от моего объекта StreamWriter, который, как я понимаю, обязательно должен быть уничтожен.

Почему StreamWriter должен оставаться открытым, если я уже сбросил его содержимое на объект MemoryStream?

Я могу придумать некоторые очень неудобные способы обойти это, но я хотел бы знать, почему это не работает так, как я этого хочу, и есть ли что-то, что я могу сделать, чтобы заставить его работать путь. Любые советы приветствуются, спасибо!

ответ

0

Другие ответы указывают, что я должен использовать конструктор с параметром leaveOpen и установить его в true, но мне это не нравится, потому что конструктор также требует аргумент bufferSize.

Однако я понял, что я могу уйти с этим так же легко:

 // new method body, returns byte array 

     var stream = new MemoryStream(); 
     using (var writer = new StreamWriter(stream)) 
     { 
      writer.WriteLine(string.Join(",", columnNames)); 
      foreach (var user in users) 
      { 
       var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList(); 
       row.Insert(0, user.UserName); 
       writer.WriteLine(string.Join(",", row)); 
      } 

      writer.Flush(); 
      stream.Position = 0; 
     } 

     return stream.ToArray(); 

     // consumer opens a new stream using the bytes 

     using (var stream = new MemoryStream(this.GetCSVStream(leaderboard, users))) 
     { 
      mockFile.Setup(f => f.InputStream).Returns(stream); 
      this.service.UpdateEntries(update.ID, mockFile.Object); 
     } 
+1

Если вы предпочитаете не передавать дополнительные аргументы, вы можете использовать 'GetBuffer' вместо' ToArray'. 'GetBuffer' возвращает внутренний буфер напрямую и представляет собой простую управляемую память и, таким образом, доступен даже после того, как расположен« MemoryStream ». «ToArray» делает копию данных, которые могут приводить к проблемам производительности и памяти в зависимости от размера ваших данных и вашего конкретного сценария. –

+0

Спасибо, я посмотрю. – Samo

2

using вызов оператора Dispose метод на объект перед выходом из операционного блока { }.

В распоряжении StreamWriter имеется также Stream.

Это означает, что ваш Stream является недействительным объектом, прежде чем он будет возвращен.

Применить заявление using только для объектов, созданных и уничтоженных в текущей области действия (не возвращайте их как минимум).

Почему StreamWriter должен оставаться открытым, если я уже сбросил его содержимое в объект MemoryStream?

Как @mikez упоминалось, по умолчанию создается StreamWriter «владеет» основной поток, но вы можете избежать этого путем добавления leaveOpen = true в конструкторе.

new StreamWriter(stream = s, encoding = Encoding.UTF8, bufferSize = 128, leaveOpen = true) 
3

По умолчанию StreamWriter «владеет» поток он передается и закрыть его утилизировать. Используйте конструктор с leaveOpen boolean parameter. Установите его в true, чтобы не закрывать базовый Stream, когда автор находится в вашем первом примере.

+0

Конструктор, который принимает этот параметр также требует BufferSize ARG. Это неудобно, как кажется? Как я должен знать, насколько это необходимо? – Samo

+0

@samo установил его к 256, например. Вам нужны большие блоки, если вы пишете большие куски данных. Но это не предел и влияет только на производительность, поэтому не беспокойтесь. – Croll

+0

Может быть упомянуто, что эта конкретная перегрузка конструктора была добавлена ​​только с .NET 4.5. Если вы не можете использовать .NET 4.5, вам не повезло. – Kyle

2

StreamWriter автоматически закрывает Stream, если вы этого не скажете. Создайте его, как это вместо того, чтобы оставить Stream открытыми:

using (var writer = new StreamWriter(stream, System.Text.Encoding.UTF8, 1024, true)) 

В качестве альтернативы, если вы не хотите, чтобы передать дополнительные аргументы, используйте GetBuffer для доступа MemoryStream внутреннего буфера. Избегайте ToArray, так как это создает копию данных и в зависимости от вашего сценария может быть неэффективным.

+0

Хмм, размеры магического буфера. Это кажется неудобным. – Samo

+1

@Samo Я согласен, но, к сожалению, 'StreamWriter' не имеет конструктора, который позволяет передавать как поток, так и параметр' leaveOpen'. 1024 - это значение, используемое внутри, так что оно соответствует поведению конструктора, который вы используете в своем примере кода. См. Http://referencesource.microsoft.com/#mscorlib/system/io/streamwriter.cs,62bd8ad495f57b21 –