Moses Moses -4 years ago 133
C# Question

Partial Class to avoid instancing multiple classes creating strings for the same file

Project type: C# Class Library being called from an ASP.NET application
Framework version: 4.5.1

I've been searching all over about partial classes and all references I have found is that they are a code smell but all of them I have found are talking about god classes which this is not. I need help deciding what the best approach would be for this scenario.

I know partial classes are generally for designer generated code but...I can't decide between the options listed below. Is my option 2 with a partial class a really bad idea?

I am writing a program to create atypical csv files meaning the file does not contain columnar data. Each line contains different data and a varying number of columns.

Whichever option used each method needs access to 2 datasets and a Document class which contains a public List which each method adds a line (string) to.

What I do know:
Each line has a non-unique type identifier such as "H00","H10","D50","D55"
Each line has fixed number of columns based on the line type identifier
Some lines are related to others while others are not
There are about 25 (give or take 5) types of lines, so there would be about 25 files part of the partial class plus one for a constructor. This number could grow but not more than 40 at absolute most.

What I do not want:
One big class with a method for each line type
One big method with a bunch of helper methods

Option 1: Create a separate Class for each line type
Option 1 problem: this creates lots of class instances

Option 2: Create a partial class named "Line" with a seperate *.cs files for each line type containing a method matching the line type it is creating. With this example, I think I would use class level properties for the datasets and document initialized in a constructor.
Option 2: Partial classes are considered a code smell because they are difficult to manage and make code more complex.

Typical method, left out parameters to keep this example simple.

public class Document
{
//The document class would not be part of the partial class
//The document has other properties, I left them out to keep it simple
public List<string> Lines { get; set; }

public Document()
{
this.Lines = new List<string>
}
}

partial class LineMethods // file named: H01.cs
{
public void H01(DataSet ds1, DataSet ds2, Document doc)
{

string[] fields = new string[3];
fields[0] = "855";
fields[1] = "value from datatable";
fields[2] = "value from datatable";

string line = string.Join(",", fields);
doc.Lines.Add(line);
}
}

partial class LineMethods // File named: H10
{
public void H10(DataSet ds1, DataSet ds2, Document doc)
{
string[] fields = new string[27];
fields[0] = "855";
fields[10] = "other from datatable";
fields[16] = "other data";

//Line variable would be added to the "document.Lines.Add"
string line = string.Join(",", fields);
doc.Lines.Add(line);
}
}

Answer Source

You should ask yourself what would be most manageable in the long run. What will be easiest to read and maintain.

Solution 1:

I'd go this way:

public class Document
{
    //The document class would not be part of the partial class
    //The document has other properties, I left them out to keep it simple
    public List<string> Lines { get; set; }

    public Document()
    {
        this.Lines = new List<string>
    }
}

public interface ILine
{
    string[] ProduceLine(DataSet ds1, DataSet ds2);
}

public class H01 : ILine
{
    public string[] ProduceLine(DataSet ds1, DataSet ds2)
    {
        string[] fields = new string[3];
        fields[0] = "855";
        fields[1] = "value from datatable";
        fields[2] = "value from datatable";

        return fields;
    }
}

public class H10 : ILine
{
    public string ProduceLine(DataSet ds1, DataSet ds2)
    {
        string[] fields = new string[27];
        fields[0] = "855";
        fields[10] = "other from datatable";
        fields[16] = "other data";

        return fields;
    }
}

var doc = new Document();

foreach (ILine line in lines)
{
    doc.Lines.Add(string.Join(",", line.ProduceLine(ds1, ds2)));
}

Edit (where lines come from):

Well, really depends on you. Simplest example:

var lines = new List<ILine>(new []
{
    new H10(),
    new H01()
});

It would better if you didn't have to KNOW about every single implementation and simply have Unity inject all of them in your constructor. But it really depends on your business case. How do you determine which of the implementations to use? Do you need all, in all cases? Etc.

e.g.:

public class MyWorker
{
  // This would require that you setup Inversion of Control container and use it to resolve MyWorker.
  // See: http://stackoverflow.com/questions/1961549/resolving-ienumerablet-with-unity
  public MyWorker(IEnumerable<ILine> lines)
  {
     this.lines = lines;
  }
}

Solution 2:

Alternatively, if you REALLY like your partial construct, you can use delegates like this:

delegate string[] ProduceLine(DataSet ds1, DataSet ds2);

partial class LineMethods // file named: H01.cs
{
    public string[] H01(DataSet ds1, DataSet ds2)
    {
        string[] fields = new string[3];
        fields[0] = "855";
        fields[1] = "value from datatable";
        fields[2] = "value from datatable";

        return fields;
    }
}

partial class LineMethods // File named: H10
{
    public string[] H10(DataSet ds1, DataSet ds2)
    {
        string[] fields = new string[27];
        fields[0] = "855";
        fields[10] = "other from datatable";
        fields[16] = "other data";

        return fields;
    }
}

var h01Method = new ProduceLine(LineMethods.H01);
doc.Lines.Add(string.Join(",", h01Method.Invoke(ds1, ds2));

You decide what is easier to read and maintain.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download