# Writing data to a file

I have the following two methods that write to file various data line by line. Both methods differ from each other only by few lines what would be the best way to refactor them that when in future I want to add another method (for example, CreateXYZLegacyInFile) it would be least time consuming.

public string CreateCVTLegacyInFile()
{
try
{
string dllLocation = string.Format("{0}\\{1}\\cvt\\", dllWorkingDirectory, FactoryCode);

string inFile = string.Empty;
inFile += AddLine(mainData.LightningInsulationLevel + "," + mainData.SwitchingInsulationLevel);
inFile += AddLine(mainData.MinimumAmbientTemperature + "," + mainData.MaximumAmbientTemperature + "," + mainData.AverageAmbientTemperature);
inFile += AddLine(capacitanceData.TotalNumberOfElements + "," + capacitanceData.NumberOfElementsSelected);
inFile += AddLine(capacitanceData.CapacitanceTotal + "," + capacitanceData.CapacitanceSecond + "," +
capacitanceData.CapacitanceFirst + "," + capacitanceData.CapacitanceElement);
inFile += AddLine(mainData.InsulatorCreepageDistance + "," + mainData.InsulatorFlashoverDistance);

if (vtData.IsSpecialDesign)
{
string areEnabledMarkings = string.Empty;

areEnabledMarkings += SpData.IsSelectionOfSecondaryTurns + ",";
areEnabledMarkings += SpData.IsSelectionOfPrimaryWire + ",";
areEnabledMarkings += SpData.IsSelectionOfSecondaryWire;

if (SpData.IsSelectionOfSecondaryTurns == "T")
{
}

if (SpData.IsSelectionOfPrimaryWire == "T")
{
}

if (SpData.IsSelectionOfSecondaryWire == "T")
{

foreach (var item in SpData.SecondaryWindingWireDimensions)
{

}

}

}

foreach (WindingData windingData in windingsData)
{
if (vtData.IsExtendedNumberOfClasses)
{
}

inFile += extractBurdenAndAccuracyClassData(windingData.MainTerminalBurdenAndAccuracy);
inFile += extractBurdenAndAccuracyClassData(windingData.MainTerminalOtherWindingBurdenAndAccuracy);

if (vtData.IsExtendedNumberOfClasses)
{
inFile += extractBurdenAndAccuracyClassData(windingData.TappedTerminalBurdenAndAccuracy);
inFile += extractBurdenAndAccuracyClassData(windingData.TappedTerminalOtherWindingBurdenAndAccuracy);
}

foreach (string terminalMarking in windingData.TerminalMarking)
{
}
}

string gyrStatus = string.Empty;
gyrStatus += translateGYRStatus(commonTransformerProperties.TransformerTypeAndSizeStatus);
gyrStatus += translateGYRStatus(commonTransformerProperties.AverageAmbientTemperatureStatus);
gyrStatus += translateGYRStatus(capacitorVoltageDivider.Insulator.Status);
gyrStatus += " "; //Shell NO
gyrStatus += " "; //Ipn
gyrStatus += " "; //OMS
gyrStatus += (intermediateVoltageTransformer.IsPotentialGround) ? "Y" : "G";
gyrStatus += translateGYRStatus(intermediateVoltageTransformer.Status);
gyrStatus += translateGYRStatus(capacitorVoltageDivider.CVDStatus);
gyrStatus += translateGYRStatus(secondaryWindings.FuseStatus);
gyrStatus += " ";// Alert STATUS

return inFile;
}
catch (Exception ex)
{
throw ex;
}
}

public string CreateVTLegacyInFile()
{
try
{
string dllLocation = string.Format("{0}\\{1}\\vt\\", dllWorkingDirectory, FactoryCode);

string inFile = string.Empty;

inFile += AddLine(mainData.LightningInsulationLevel + "," + mainData.SwitchingInsulationLevel);
inFile += AddLine(mainData.MinimumAmbientTemperature + "," + mainData.MaximumAmbientTemperature + "," + mainData.AverageAmbientTemperature);
inFile += AddLine(mainData.InsulatorCreepageDistance + "," + mainData.InsulatorFlashoverDistance);

if (vtData.IsSpecialDesign)
{
string areEnabledMarkings = string.Empty;

areEnabledMarkings += SpData.IsSelectionOfSecondaryTurns + ",";
areEnabledMarkings += SpData.IsSelectionOfPrimaryWire + ",";
areEnabledMarkings += SpData.IsSelectionOfSecondaryWire;

if (SpData.IsSelectionOfSecondaryTurns == "T")
{
}

if (SpData.IsSelectionOfPrimaryWire == "T")
{
}

if (SpData.IsSelectionOfSecondaryWire == "T")
{

foreach (var item in SpData.SecondaryWindingWireDimensions)
{

}

}

}

foreach (WindingData windingData in windingsData)
{
if (vtData.IsExtendedNumberOfClasses)
{
}

inFile += extractBurdenAndAccuracyClassData(windingData.MainTerminalBurdenAndAccuracy);
inFile += extractBurdenAndAccuracyClassData(windingData.MainTerminalOtherWindingBurdenAndAccuracy);

if (vtData.IsExtendedNumberOfClasses)
{
inFile += extractBurdenAndAccuracyClassData(windingData.TappedTerminalBurdenAndAccuracy);
inFile += extractBurdenAndAccuracyClassData(windingData.TappedTerminalOtherWindingBurdenAndAccuracy);
}

foreach (string terminalMarking in windingData.TerminalMarking)
{
}
}

string gyrStatus = string.Empty;
gyrStatus += translateGYRStatus(commonTransformerProperties.TransformerTypeAndSizeStatus);
gyrStatus += translateGYRStatus(commonTransformerProperties.AverageAmbientTemperatureStatus);
gyrStatus += translateGYRStatus(Insulator.Status);
gyrStatus += " "; //Shell NO
gyrStatus += " "; //Ipn
gyrStatus += " "; //OMS
gyrStatus += translateGYRStatus(secondaryWindings.FuseStatus);
gyrStatus += " ";// Alert STATUS

return inFile;
}
catch (Exception ex)
{
throw ex;
}
}

• First and foremost, quit using inFile as a string and instead use StringBuilder. Jan 22 '16 at 19:29

First, as mentioned in comments above, for efficiency you should be using StringBuilder when appending lots of strings repeatedly to make one big string.

The simplest answer I can give is to show you an example of how I might tackle the problem. The following uses a helper class FileContentWithMarks with the "know-how" for your custom formatting, and an instance of StringBuilder to build up the string.

What follows includes lots of second guesses based on the code snippet provided but hopefully it shows you a way to get started.

public enum ExportTransformerType
{
Unknown,
NonCapacitive,
Capacitive
}

public string CreateLegacyInFile()
{
try
{
var inFile = new FileContentWithMarks();

// Here, I am assuming this is a way to derive the file type. YMMV.
ExportTransformerType transformerType = DeriveTransformerType(mainData.TransformerType);

inFile.AppendLineWithMarks(GetFolderLocation(transformerType));

// Lines which are common beteween all types can go here verbatim.
// You should also consider whether these can be organised into sub-methods
// for a better overall picture of what's going on.
inFile.AppendLineWithMarks(mainData.CalculationReference);
inFile.AppendLineWithMarks(DateTime.Now.ToString("MM/dd/yy").Replace('-', '/'));
inFile.AppendLineWithMarks(mainData.Designer);
inFile.AppendLineWithMarks(mainData.TransformerType);

inFile.AppendLine(mainData.TransformerSize +
(transformerType == ExportTransformerType.Capacitive ?
string.Empty
:
mainData.TransformerExtension));

inFile.AppendLineWithMarks(mainData.TransformerModel);
inFile.AppendLineWithMarks(mainData.MainStandard);

// ... etc. More shared logic lines copied here

if (transformerType == ExportTransformerType.Capacitive)
{
inFile.AppendLineWithMarks(capacitanceData.NominalIntermediateVoltage);
}

/// ... etc.

inFile.AppendLineWithMarks(gyrStatus);

// Finally, ask the helper to generate the complete string.
return inFile.ToString();
}
catch (Exception ex)
{
throw ex;
}
}

public ExportTransformerType DeriveTransformerType(string typeName)
{
if (typeName == "CVT")
return ExportTransformerType.Capacitive;

if (typeName == "VT")
return ExportTransformerType.NonCapacitive;

return ExportTransformerType.Unknown;
}

public string GetFolderLocation(ExportTransformerType transformerType)
{
string subFolder;

switch (transformerType)
{
case ExportTransformerType.NonCapacitive:
subFolder = "vt";
break;
case ExportTransformerType.Capacitive:
subFolder = "cvt";
break;
default:
subFolder = "unknown";  // or raise an exception if you prefer
break;
}

return string.Format("{0}\\{1}\\{2}\\", dllWorkingDirectory, FactoryCode, subFolder);
}

// Wrapper for StringBuilder with specific line formatting/markings
public class FileContentWithMarks
{
private StringBuilder _content = new StringBuilder();

public void AppendLine(string line)
{
// The logic for how you format lines without marks can go here
_content.AppendLine(line);
}

public void AppendLineWithMarks(string line)
{
// The logic for how you format lines with marks can go here
_content.AppendLine("{" + line + "}");
}

public override string ToString()
{
return _content.ToString();
}
}


Why bother defining this new helper class FileContentWithMarks? Let's look at the style of code required if inFile was a pure StringBuilder:

        inFile.Append(AddLineWithMarks(mainData.Designer));


The more succinct form below is slightly easier to read and that in itself might be a good enough reason to use a helper...:

        inFile.AppendLineWithMarks(mainData.Designer);
inFile.AppendLineWithMarks(mainData.TransformerType);


... but it also helps to encapsulate all the formatting logic within one place. This might benefit us in future when we need to add newer line categories (by defining new methods), or perhaps define different mark characters to be used (by adding a new class constructor that allows the default characters to be overridden).

Perhaps we will write another version of the FileContentWithMarks class, make both of them implement the same interface, but this version streams the lines both to a console and to a log file at once, doing away with the StringBuilder altogether. Then we can choose at runtime which class to instantiate without needing to change any of the instance method calls.

Designing in this way, separating out concerns, is good practice and beneficial to code reuse and maintenance.

• Welcome to CR! Good job on your first post! It would be nice if you could expand a bit on your idea of using this FileContentWithMarks class to wrap the StringBuilder, e.g. how it helps breaking down responsibilities and concerns, etc. Feb 18 '16 at 20:29