Handling of memorystream with streamwriter efficiency

Posted on

Problem

My little program I’m working on generates charts (GUI) and (on another tab) lists the values my chart is fed from.

2011.01.01 0:00:00     456
2011.01.02 0:00:00     45
2011.01.03 0:00:00     46
2011.01.04 0:00:00     6
2011.01.05 0:00:00     1500
...

When I click on the “Generate Chart” button, after the chart generator code has run, the following method is also invoked (that generates the table above):

void FillGridDetails(List<NameSeries> chartDataIn)

This fills a grid with the values. (DateTime as key, and float as value, nothing special…)

// Creating the rows.
gridDetails.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(30) }); // First row (Date/Time)
int maxCount = chartDataIn[maxIndex].seriesData.Count;
int rowCount = 1;

// Create the memory stream to write the data in (later this will be written to a file if the users clicks the export button).
ms = new MemoryStream(); // This is a class level variable.
StreamWriter writer = new StreamWriter(ms) { AutoFlush = true };
foreach (var aDataRow in chartDataIn[maxIndex].seriesData)
{
    // Adding the first column's rows.
    Label lblDate = new Label() { Content = aDataRow.Key.ToString(dateFormatIn), HorizontalAlignment = HorizontalAlignment.Center };
    gridDetails.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(30) });
    gridDetails.Children.Add(lblDate);
    Grid.SetRow(lblDate, rowCount);
    Grid.SetColumn(lblDate, 0);
    writer.Write(aDataRow.Key.ToString() + ";");

    // Adding the further columns' rows.
    for (int i = 0; i < chartDataIn.Count; i++)
    {
        string value = chartDataIn[i].seriesData.ContainsKey(aDataRow.Key) ? chartDataIn[i].seriesData[aDataRow.Key].ToString() : "-";
        Label lblData = new Label() { Content = value };
        gridDetails.Children.Add(lblData);
        Grid.SetRow(lblData, rowCount);
        Grid.SetColumn(lblData, i + 1);

        if (i == chartDataIn.Count - 1)
        {
            writer.WriteLine(value == "-" ? "" : value);
        }
        else
        {
            writer.Write(value == "-" ? ";" : value + ";");
        }
    }

    rowCount++;
}

On this tab there is a CSV Export button, click:

SaveFileDialog dlg = new SaveFileDialog()
{
    FileName = "EM_ExportedData",
    DefaultExt = ".csv",
    Filter = "CSV comma separated values (.csv)|*.csv"
};

if ((bool)dlg.ShowDialog())
{
    //try
    //{
    using (FileStream file = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write))
    {
        ms.WriteTo(file);
        ms.Dispose();
        btnExportCSV.Content = Strings.CSVExported;
        btnExportCSV.IsEnabled = false;
        MessageBox.Show(String.Format(Strings.successfulSavedCSVFile, dlg.FileName));
    }
}

Could it cause any sort of disadvantages in memory efficiency? I’m thinking of, e.g., not closing the filestream or something. If I close the fileStream, later I can’t access the memory stream because that’s also closed. Any recommendations for this code part is much appreciated.

(What I wanted to do is this: store the values in the memory or somewhere (right after the generation) and when the user wants to export it is simply written to the file. I was also thinking of copy paste the foreach loop but only with the memory-writing parts.)

Solution

It looks like you are wasting your efforts on premature optimization. Which also results in a quite ugly code.

I think you should separate your grid-filling logic from csv export. There should be no memory streams, steam writers, etc. in your first method. Also notice that you have code duplication in it, so you might want to extract label creation logic to separate method. Ideally you’d want to remove this logic all together and use WPF ListView (or ItemsControl) with data-binding instead.

As for your second method – you do not need any buffers. Just iterate through your data, create strings and write them directly to your FileStream. You might also want to encapsulate this logic in some separate class and perform it in the background thread so your UI does not freeze for large data.

Leave a Reply

Your email address will not be published. Required fields are marked *