【问题标题】:How to avoid repeated code?如何避免重复代码?
【发布时间】:2011-08-29 02:18:25
【问题描述】:

我对编程还是很陌生,我注意到我在重复代码:

protected void FillTradeSetups()
{
    DBUtil DB = new DBUtil();
    DataTable dtTradeSetups;

    dtTradeSetups = DB.GetTradeSetups();
    ddlSetups.DataValueField = "tradeSetupId";
    ddlSetups.DataSource = dtTradeSetups;
    ddlSetups.DataBind();
}

protected void FillTimeFrames()
{
    DBUtil DB = new DBUtil();
    DataTable dtTimeFrames;

    dtTimeFrames = DB.GetTimeFrames();
    ddlTimeFrames.DataValueField = "tfCode";
    ddlTimeFrames.DataSource = dtTimeFrames;
    ddlTimeFrames.DataBind();
} 

protected void FillTradeGrades()
{
    DBUtil DB = new DBUtil();
    DataTable dtTradeGrades;

    dtTradeGrades = DB.GetTradeGrades();
    ddlTradeGrades.DataValueField = "tradeGrade";
    ddlTradeGrades.DataTextField = "descr";
    ddlTradeGrades.DataSource = dtTradeGrades;
    ddlTradeGrades.DataBind();
}

protected void FillExecutionGrades()
{
    DBUtil DB = new DBUtil();
    DataTable dtExecutionGrades;

    dtExecutionGrades = DB.GetExecutionGrades();
    ddlExecutionGrades.DataValueField = "executionGrade";
    ddlExecutionGrades.DataTextField = "descr";
    ddlExecutionGrades.DataSource = dtExecutionGrades;
    ddlExecutionGrades.DataBind();
} 

我怎样才能更聪明一点呢?你能帮我重新编写代码,这样它就不会重复太多了吗?

更新

哇,感谢您的回复,我想我会发布我正在考虑实施的内容。我还为自己创建了另一个小工人来删除其他一些丑陋的重复代码。您对此有何看法?

void FillDropDownList(DropDownList ddl, DataTable dt, string dataValueField, string dataTextField, string defValue)
{
    ddl.DataValueField = dataValueField;
    ddl.DataSource = dt;
    if (!string.IsNullOrEmpty(dataTextField))
    {
        ddl.DataTextField = dataTextField;
    }

    ddl.DataBind();
    ddl.SelectedValue = defValue;
}

private string GetTradeItem(DataTable tradeDetails, string attribute)
{
    return tradeDetails.Rows[0][attribute].ToString();
}

然后用类似这样的方式调用它:

int tradeId = int.Parse(Request.QueryString["tradeId"]);
DBUtil DB = new DBUtil();
DataTable tradeDetails = DB.GetTrade(tradeId);
FillDropDownList(ddlTradeGrades, DB.GetTradeGrades(), "tradeGrade", "descr", GetTradeItem(tradeDetails, "tradeGrade"));

当丑陋的东西变成更优雅的东西时,编码感觉很棒。

【问题讨论】:

  • 在您职业生涯的早期提出的一个好问题。避免“sn-ps”和“复制粘贴编程”的能力是一项需要掌握的重要技能。
  • 你想看Refactoring
  • 投票关闭为重复,但好的答案应该与原始答案合并。
  • 这个问题不是关于如何避免重复代码,而不是关于这个具体的例子吗?不得不说,答案太具体了,我有点失望。

标签: c# asp.net code-reuse


【解决方案1】:

可能是这样的吧?

void SetupDataSource(DropDownList ddl, DataTable dt, string dataValueFieldm, string dataTextField)
{
    if (ddl != null)
    {
        ddl.DataValueField = dataValueField;
        ddl.DataSource = dt;
        if (!string.IsNullOrEmpty(dataTextField)) 
        {
            ddl.DataTextField  = dataTextField;
        }

        ddl.DataBind();
    }
    else
    {
       throw new ArgumentNullException("ddl");
    }
}

【讨论】:

  • @John 来避免 NullReferenceException 但我想检查的要求是有条件的,有时最好不要检查。
  • @mellamokb 我刚刚添加了DataTextField
  • @Bala:我的意思是原始代码没有检查,所以假设它不是必需的。
  • 我认为空值检查是不好的做法,因为它隐藏了一个编程错误(通过隐含合同,传入的值是“有效的”)。如果有的话,应该是if (ddl == null) throw new ArgumentNullException("ddl");
【解决方案2】:

FWIW,也许是这样的:

void BindControl(DropDownList ddl, string valueField, string textField, DataTable data) {
    ddl.DataValueField = valueField;
    ddl.DataTextField = textField ?? valueField; // textField can be null
    ddl.DataSource = data;
    ddl.DataBind();
}

DBUtil DB = new DBUtil();
BindControl(ddlTradeGrades, "tradeGrade", "descr", DB.GetTradeGrades());
...

但是,管道本身并没有完全重复,应该考虑到将来修改/维护的便利性。

编码愉快。

【讨论】:

    【解决方案3】:

    比如:

    void FillData(DataTable dataSource, DropDownList ddl, string dataValueField, string dataTextField)
    {
        ddl.DataSource = dt;
        ddl.DataValueField = dataValueField;
        ddl.DataTextField = dataTextField;
        ddl.DataBind();
    }
    

    那么你可以这样称呼它:

    FillData(DB.GetTradeSetups(), ddlSetups, "tradeSetupId", string.Empty)
    

    【讨论】:

      【解决方案4】:

      创建一个设置你想要的东西的方法:

      void SetValues(DropDownList ddl, string datavalue, string text, object ds)
      {
          ddl.DataValueField = dataValue;
          ddl.DataTextField = text;
          ddl.DataSource = ds;
          ddl.DataBind();
      }
      

      然后你可以调用它:

      SetValues(ddlTradeGrades, "tradeGrade", "descr", dtTradeGrades);
      

      【讨论】:

      • @John:一种类型。不幸的是,不是框架中定义的类型。更改为 Object 以匹配 DropDownList 使用的内容。
      【解决方案5】:

      你可以这样做:

         private void BindMyLists()
          {
              DBUtil DB = new DBUtil();
              BindDropDownList(this.ddlExecutionGrades, DB.GetExecutionGrades(), "executionGrade", "descr");
              BindDropDownList(this.ddlTradeGrades, DB.GetTradeGrades(), "tradeGrade", "descr");
              //etc
          }
          protected void BindDropDownList(DropDownList dropDownList, DataTable dataTable, string dataValueField, string dataTextField)
          {
              dropDownList.DataValueField = dataValueField;
              dropDownList.DataTextField = dataTextField;
              dropDownList.DataSource = dataTable;
              dropDownList.DataBind();
          }
      

      【讨论】:

      • ddlTradeGrades -> dropDownList in BindDropDownList
      • 是啊...我提交的速度太快了,而且有很多错误。投反对票是应得的:)。已修复以供将来参考。我很抱歉。
      【解决方案6】:
      【解决方案7】:

      可能是这样的……

      protected void FillDdl(DropDownList ddl, string dataValueField, Func<DataTable> dataTableMethod)
      {
          FillDdl(ddl, dataValueField, null, dataTableMethod);
      }
      
      protected void FillDdl(DropDownList ddl, string dataValueField, string dataTextField, Func<DataTable> dataTableMethod)
      {
          DataTable dt = dataTableMethod();
      
          ddl.DataSource = dt;
      
          ddl.DataValueField = dataValueField;
          ddl.DataTextField = dataTextField ?? dataValueField;
      
          ddl.DataBind();
      }
      

      然后直接这样调用

      DBUtil DB = new DBUtil();
      
      FillDdl(ddlSetups, "tradeSetupId", DB.GetTradeSetups);
      FillDdl(ddlTimeFrames, "tfCode", DB.GetTimeFrames);
      FillDdl(ddlTradeGrades, "tradeGrade", "descr", DB.GetTradeGrades);
      FillDdl(ddlExecutionGrades, "executionGrade", "descr", DB.GetExecutionGrades);
      

      或者你仍然可以有瘦的个别方法

      protected void FillTradeSetups()
      {
          DBUtil DB = new DBUtil();
          FillDdl(ddlSetups, "tradeSetupId", DB.GetTradeSetups);
      }
      
      protected void FillTimeFrames()
      {
          DBUtil DB = new DBUtil();
          FillDdl(ddlTimeFrames, "tfCode", DB.GetTimeFrames);
      } 
      
      protected void FillTradeGrades()
      {
          DBUtil DB = new DBUtil();
          FillDdl(ddlTradeGrades, "tradeGrade", "descr", DB.GetTradeGrades);
      }
      
      protected void FillExecutionGrades()
      {
          DBUtil DB = new DBUtil();
          FillDdl(ddlExecutionGrades, "executionGrade", "descr", DB.GetExecutionGrades);
      } 
      

      【讨论】:

      • +1 但仅供参考,使用委托可能会触发一些与调试和单元测试相关的模糊 CLR 错误。在这种情况下,被调用的方法非常简单,实际上不需要委托,所以我会在没有委托的情况下这样做。如果它们是具有不同签名的方法,或者需要的不仅仅是方法调用,那么委托将是可行的方法。
      • 谢谢。那么在方法中传递一个字符串methodName,然后使用反射(缓慢且可能矫枉过正)或case语句在DBUtil上调用适当的方法会更好吗?
      • 不,最好简单地采用DataTable 类型的参数并在调用者中使用DB.GetTradeSetups()DB.GetTradeGrades() 等。
      • 对,正如@mellam:所说,您不需要委托或使用方法名称提供的全部通用性。