C#使用单行更改重复功能

时间:2017-01-16 15:09:55

标签: c# function lambda refactoring

我注意到我一遍又一遍地重复相同的几行代码,我想重构以防止维护。但是,每次迭代之间的更改都在所有验证的中间,因此我不确定实现此目的的最佳方法。我觉得有一些方法可以编写一个可以接受输入来更改内部代码的泛型函数,但我能想到的唯一方法是每个函数的枚举和switch语句。

是否有更好的方法可以使这个重复代码块更具可读性和可维护性?

重复的具体块:

    if (_read.HasRows)
    {
        while (_read.Read())
        {
            //DO SOMETHING...;
        }
    }
    _read.Close();

示例用法:

public List<object> TableList()
{
    List<object> list = new List<object>();

    _cmd.CommandText = "SELECT * FROM sys.tables";

    try
    {
        _read = _cmd.ExecuteReader();
    }
    catch (Exception e)
    {
                     //please ignore this try catch for now
        list[0] = e; //this is a temporary measure until I finalize error handling
    }

//block to generalize
    if (_read.HasRows)
    {
        while (_read.Read())
        {
            list.Add(_read.GetValue(0)); //unique line
        }
    }


    _read.Close();

    return list;
}

另一个例子:

private List<object[]> _Columns = new List<object[]>()    
private bool SetColumns()
{
    _oldCommand = _cmd.CommandText;
    _cmd.CommandText = "exec sp_columns " + tableName;
        try
        {
            _read = _cmd.ExecuteReader();
        }
        catch (Exception e)
        { }  //again, ignore the messy error handling.  That is coming next.


//block to generalize
    if (_read.HasRows)
    {
        while (_read.Read())
        {
            _Columns.Add(new object[2] { _read.GetValue(3), _read.GetValue(5) }); //unique line
        }
    }



    _read.Close();
    _cmd.CommandText = _oldCommand;
    if (_Columns.Count != 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

只是提醒问题的具体范围的底部。 我如何概括重复的代码块,同时仍然允许每个不同的用途存在唯一的行?我只举了两个例子,但是这个问题有几个例子,我想在深入之前进行重构。

看到答案后的最终实施:

    /// <summary>
    /// Sets a list of tables in the connected database.
    /// </summary>
    /// <returns>Returns a List<string> of the tables.  If there is an error, then the first element of the list will conatin the exception.</returns>
    public List<string> TableList()
    {
        List<string> list = new List<string>();

        _cmd.CommandText = "SELECT * FROM sys.tables";

        ExecuteReader(_read => { list.Add(_read.GetString(0)); });

        _read.Close();

        return list;
    }


    /// <summary>
    /// sets column names and types to the _columns private variable [0] = name, [1] = type
    /// </summary>
    private void SetColumns()
    {
        _oldCommand = _cmd.CommandText;

        _cmd.CommandText = "exec sp_columns " + TableName;

        ExecuteReader(_read => columns.Add(new string[2] { _read.GetString(3), _read.GetString(5) }));

        _cmd.CommandText = _oldCommand;

        _read.Close();

    }


    /// <summary>
    /// Executes the reader with error handling and type returns.
    /// </summary>
    /// <param name="readline">the code that actually pulls the data from each line of the reader</param>
    private void ExecuteReader(Action<SqlDataReader> readline)
    {
        if (_con.State.HasFlag(ConnectionState.Closed)) { _con.Open(); }

        _read = _cmd.ExecuteReader();

        while (_read.Read())
        {
            readline(_read);
        }

        _con.Close();

    }

我选择了这种方法,因为我觉得我的经验水平最具可读性。正如许多其他人所推荐的那样,我已经对Dapper做过一些调查。我很可能会在未来的项目中使用它,因为我喜欢根据自己的经验编写自己的样板代码至少一次。

谢谢大家的惊人答案。我希望其他人在将来发现这有用。

5 个答案:

答案 0 :(得分:1)

您可以通过传入lambda表达式来分解填充代码,例如:

SetColumns(reader => _Columns.Add(new object[2] { _read.GetValue(3), _read.GetValue(5) }));

SetColumns(reader => list.Add(reader.GetValue(0)));

查看while循环内的更新行:

private List<object[]> _Columns = new List<object[]>();
private bool SetColumns(Action<DbDataReader> populateColumns)
{
    _oldCommand = _cmd.CommandText;
    _cmd.CommandText = "exec sp_columns " + tableName;
        try
        {
            _read = _cmd.ExecuteReader();
        }
        catch (Exception e)
        { }  //again, ignore the messy error handling.  That is coming next.


//block to generalize
    if (_read.HasRows)
    {
        while (_read.Read())
        {
            populateColumns(_read);
        }
    }



    _read.Close();
    _cmd.CommandText = _oldCommand;
    if (_Columns.Count != 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

答案 1 :(得分:1)

当您考虑重用时,有两种方法可以解决问题。

  • 只需将它作为一种方法,即可在任何地方使用。
  • 继承

现在在这种情况下,继承听起来有点咄咄逼人。但是制定方法可能是正确的。

这是我的镜头:

public class FillerFromDb
{
    public IList ListToBeFilled { get; set; }
    SqlCommand Command { get; set; }
    SqlDataReader Reader { get; set; }
    public FillerFromDb ( IList listToBeFilled, SqlCommand commandToRun )
    {
        ListToBeFilled = listToBeFilled;
        Command = commandToRun;
    }

    public void RunFor ( Func<SqlDataReader, object> rowProcessing )
    {
        ReadFromDb();
        ProcessRow(rowProcessing);
    }

    private void ReadFromDb ()
    {
        try
        {
            Reader = Command.ExecuteReader();
        }
        catch ( Exception e )
        {
            // handle the exception
        }
    }

    private void ProcessRow ( Func<SqlDataReader, object> rowProcessing )
    {
        if ( Reader.HasRows )
        {
            while ( Reader.Read() )
            {
                var result = rowProcessing(Reader);
                ListToBeFilled.Add(result);
            }
        }

        Reader.Close();
    }
}

被叫者:

var filler = new FillerFromDb(list/*or _Columns*/, _cmd);

filler.RunFor(row => row.GetValue(0));

如果你要进行重构,将你的工作函数包装在这样的类中,你会感觉更好。

答案 2 :(得分:0)

试试这个:

private void CommonBlock(Action act)
    {
        // some code
        if (act!=null) act.Invoke();
        //next code
    }

使用特定代码调用:

CommonBlock(()=>{Debug.WriteLine("you action");/*Anything you want to do*/});

答案 3 :(得分:0)

最简单的方法是创建一个扩展方法:

public static class Extensions
{
  public static IDataReader Read(this IDataReader reader, Action<IDataReader> action)
  {
    if(reader.HasRows)
    {
      while(reader.Read())
      {
        action(reader);
      }
    }
  }
}

使用原始样本的一个例子是:

public List<object> TableList()
{
    List<object> list = new List<object>();

    _cmd.CommandText = "SELECT * FROM sys.tables";

    try
    {
        _read = _cmd.ExecuteReader();
    }
    catch (Exception e)
    {
                     //please ignore this try catch for now
        list[0] = e; //this is a temporary measure until I finalize error handling
    }

    _read.Read(r => list.Add(r.GetValue(0)));        
    _read.Close();

    return list;
}

答案 4 :(得分:0)

通过Action或Func将差异传递给函数。写一个通用版本然后传入不同的执行版本。