Home > Enterprise >  C#: how can I overload a method to accept several types but duplicate as little code as possible?
C#: how can I overload a method to accept several types but duplicate as little code as possible?

Time:06-26

Instances of SqlDataReader can be indexed by both integers and strings (see below: reader[col]). So, I want the wrapping function to accept both List<string> and List<int> as an argument. I really don't want type-checking at runtime, though, and, as C# doesn't have something like type unions, I assume overloading is the best way to allow a function to accept different types without involving generics and at-runtime type-checking.

So... I can easily just copy the method with List<int> instead of List<string> and overload it that way, but I don't want (and shouldn't) duplicate code like this. There must be a better way.

public List<List<object>> Query(string query, List<string> relevantColumns)
    {
        var rows = new List<List<object>>();

        using (SqlConnection connection = 
            new SqlConnection(this.ConnectionString))
        {
            var command = new SqlCommand(query, connection);
            command.Connection.Open();

            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    List<object> row = (from col
                                        in relevantColumns
                                        select reader[col]).ToList();
                    rows.Add(row);
                }
            }
        }
        return rows;
    }

CodePudding user response:

You could have a third overload which takes a Func<SqlDataReader, List<object>>. Then call the other two overloads in terms of that overload. Something like...

public static class SqlUtilities
{
    public static List<List<object>> Query(string query, List<string> relevantColumns)
    {
        return Query(query, reader => (from col in relevantColumns select reader[col]).ToList());
    }

    public static List<List<object>> Query(string query, List<int> relevantColumns)
    {
        return Query(query, reader => (from col in relevantColumns select reader[col]).ToList());
    }

    private static List<List<object>> Query(
        string query, 
        Func<SqlDataReader, List<object>> selector)
    {
        var rows = new List<List<object>>();

        using (SqlConnection connection = 
            new SqlConnection(this.ConnectionString))
        {
            var command = new SqlCommand(query, connection);
            command.Connection.Open();

            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    List<object> row = selector(reader);
                    rows.Add(row);
                }
            }
        }
        return rows;
    }
}

In this specific case, however, my opinion is that a better way to solve it would be to rewrite query if that is an option. relevantColumns as a list of names or a list of ordinals is unnecessary if query is only selecting the relevant columns.

  • Related