2
\$\begingroup\$

I have a function for merging data from an object (a PrintTestDataItem) into a template file for a printed label (a LabelFormatDocument called label in this snippet). The LabelFormatDocument is a third-party class that I use to set various fields on my label and I do this by setting the numerous substrings to the values that I want from my PrintTestDataItem object.

There are a lot of these substrings and my current function just sets each substring sequentially, which is obviously not an ideal way to do this sort of operation. What would be a better way to set these substrings programmatically and any other suggestions for increasing the maintainability of this function?

My first idea is to use a dictionary to store the substring names and map them to the appropriate parameter of the PrintTestDataItem, is this the right path to go down?

public void MergePrintData(PrintTestDataItem printTestData)
{
    label.SubStrings["Barcode"].Value = printTestData.IDNumber.ToString();
    label.SubStrings["Date"].Value = printTestData.TestDate.ToString();
    label.SubStrings["WRN"].Value = printTestData.WRNNumber;
    label.SubStrings["Operator"].Value = printTestData.OperatorName;
    label.SubStrings["PrintheadType"].Value = printTestData.HeadType;
    label.SubStrings["PrintheadSerial"].Value = printTestData.HeadSerial;
    label.SubStrings["Batch"].Value = printTestData.BatchNumber;
    label.SubStrings["PrintFrequency"].Value = (printTestData.PrintFrequency + "kHz");
    label.SubStrings["PrintDistance"].Value = (printTestData.PrintDistance + "mm");
    label.SubStrings["InkType"].Value = printTestData.InkType.ToString();
    label.SubStrings["InkColour"].Value = printTestData.InkColour;
    label.SubStrings["InkTemperature"].Value = (printTestData.InkTemperature + "°C");
    label.SubStrings["DifferentialPressure"].Value = (printTestData.DifferentialPressure + "mBar");
    label.SubStrings["MeniscusPressure"].Value = (printTestData.MeniscusPressure + "mBar");
    label.SubStrings["Comments"].Value = printTestData.Comments;
    label.SubStrings["EncoderDivide"].Value = printTestData.EncoderDivide;
    label.SubStrings["EncoderMultiply"].Value = printTestData.EncoderMultiply;
    label.SubStrings["Offset"].Value = printTestData.Offset;
    label.SubStrings["NumberOfGreyLevels"].Value = printTestData.NumberOfGreyLevels;
    label.SubStrings["Mirror"].Value = BooleanRemapping(printTestData.Mirror);
    label.SubStrings["DIR"].Value = BooleanRemapping(printTestData.DIR);
    label.SubStrings["ReversePrint"].Value = BooleanRemapping(printTestData.ReversePrint);
    label.SubStrings["PrintDirection"].Value = printTestData.PrintDirection.ToString();
    label.SubStrings["PrintMode"].Value = printTestData.PrintMode.ToString();
    label.SubStrings["PrintTransportMode"].Value = printTestData.PrintTransportMode.ToString();
    label.SubStrings["AmbientTemperature"].Value = (printTestData.AmbientTemperature + "°C");
    label.SubStrings["HoursRun"].Value = printTestData.HoursRun;
    label.SubStrings["LinesDown"].Value = printTestData.LinesDown;
}
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I think the solution for this case maybe is more painful than a long code of config, and this code looks like test data.

A possible workaround could be match the label string with the same property of printTestData. and then call it by reflection. But i prefer have the long config. instead of a hackish-call-property.

\$\endgroup\$
1
  • \$\begingroup\$ @JNH, I like this config suggestion from a code maintenance view; especially if it is test data. I like the idea of the explicit declaration of the participating properties vice reflection; reflection is harder to debug and other public properties could cause problems if inadvertently picked up by the reflective code. So given the config, read it into a dictionary. \$\endgroup\$
    – radarbob
    Nov 17, 2015 at 16:46
2
\$\begingroup\$

Sometimes reflection is very useful especially if you work with attributes.

I guess the PrintTestDataItem class is under your control, right? You can decorate its properties with a custom attribute.

class PrintTestDataItem
{
    [Label("Barcode")]
    public int IDNumber { get; set; }

    [Label("PrintFrequency", Suffix = "kHz")]
    public string PrintFrequency  { get; set; }
}

where the LabelAttribute requires a label name and an optional suffix:

class LabelAttribute : Attribute
{
    private readonly string _name;
    public LabelAttribute(string name)
    {
        _name = name;
    }
    public string Suffix { get; set; }
    public override string ToString()
    {
        return _name;
    }
    public static implicit operator string(LabelAttribute label)
    {
        return label.ToString();
    }
}

By declaratively specifying the labels and with a little bit of reflection you can reduce the MergePrintData method to:

public void MergePrintData(PrintTestDataItem printTestData)
{
    var properties = 
        typeof(PrintTestDataItem)
        .GetProperties()
        .Where(p => p.GetCustomAttribute<LabelAttribute>() != null);

    foreach (var property in properties)
    {
        var labelAttr = property.GetCustomAttribute<LabelAttribute>();          
        label.SubStrings[labelAttr].Value = string.Format(
            "{0}{1}", 
            property.GetValue(printTestData), 
            labelAttr.Suffix
        ); // or string interpolation with C# 6 
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.