1
\$\begingroup\$

I have a lot of classes which are principal equal, but they consist small differences so that I can´t abstract then very well.

The classes represent different kinds of DataTables. Every Class has two important methods which every "table-class" has. Here a abstracted example:

internal class FooTable
{
    internal static DataTable CreateTable() 
    {
        using (DataTable fooTable = new DataTable("fooTable"))
        {
            // number and content of rows are different from table to table.
            fooTable.Columns.Add("ColFoo");
            fooTable.Columns.Add("ColBar");
            fooTable.Columns.Add("ColSomethingElse");

            AddRow(fooTable, "ValueA", "ValueB", "ValueC");
            AddRow(fooTable, "ValueD", "ValueE", "ValueF");
            // ... more rows ...

            return fooTable;
        }
    }

    private static void AddRow(
        DataTable table,
        string ColFoo,
        string ColBar,
        string ColSomethingElse)
    {
        DataRow dataRow = table.NewRow();

        dataRow["ColFoo"] = ColFoo;
        dataRow["ColBar"] = ColBar;
        dataRow["ColSomethingElse"] = ColSomethingElse;

        table.Rows.Add(dataRow);
    }
}

The differences between the tables are the number of columns and the datatyp of column content. With this implementation I can´t use abstract classes, because of the different amount of columns. The names of the columns are also redundant in code.

Finally I think about the use of params to solve the problem with different amount of columns, but I’m not sure how to make a good implementation other AddRow-methode.

private static void AddRow(DataTable table, params object[] values)
{       
    ... 
}

So did anyone know a better way to implement this? I'm sure there is a better way.

\$\endgroup\$
1
\$\begingroup\$

I can think of trying something like this.

interface ICount
{
  int count();
}

class CountOne : ICount { int count() { return 1; }}
class CountTwo : ICount { int count() { return 2; }}

abstract class IFooTable<T> where T : ICount, new()
{
  int count() { return (new T()).count(); }
}

class FooTable<T> : IFooTable<T>
{
  void AddRow(params object[] values) {
    assert(values.length = this.count());
    ...
  }
}

class OneTable : FooTable<CountOne> { }
class TwoTable : FooTable<CountTwo> { }

But honestly I would just expect the add function to take a row. This is not something where I'd use generics, instead I'd just use mutable data structures that have constraint checks. I'm way more concerned with column names matching especially when a params statement doesn't make sure they're in the right order.

class FooTable
{
  List<string> columns;
  List<DataRow> rows;

  void AddRow(DataRow row)
  {
    assert(column names for the row match the expected values);
    rows.add(row);
  }
}

Alternatively, you could mix both approaches. Using the generics to ensure the number of columns and then adding a DataRow at a time to enforce column names match up.

\$\endgroup\$
  • \$\begingroup\$ Hi Jean-Bernard Pellerin, thanks for your answer. At the moment I can't really see how first part of you answer helps me. From my point of view second part seems to be clear and I will consider this. Maybe you can explain part one a little bit more. \$\endgroup\$ – Micha Oct 8 '13 at 5:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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