如何将方法作为参数传递?

4

我刚刚注意到我在我的ASP.NET应用程序中重复了很多C#代码,因此想要创建一个通用方法。我有一系列像这样的私有方法:

private void PopulateMyRepeatedControl()
{
    DBUtil DB = new DBUtil();
    DataTable symbols = GetSelectedSymbols();
    DataTable tradeGrades = GetSelectedTradeGrades();
    DataTable executionGrades = GetSelectedExecutionGrades();        

    chtMyRepeatedChart.DataSource = DB.MyRepeatedCall (
        int.Parse(txtStartBalance.Text),
        int.Parse(ddlTradeTypes.SelectedValue),
        ddlRepeatedTrades.SelectedValue,
        radSide.SelectedValue,
        ddlTradeSetups.SelectedValue,
        symbols,
        ddlChartTimeFrames.SelectedValue,
        int.Parse(ddlHours.SelectedValue),
        int.Parse(ddlYears.SelectedValue),
        int.Parse(ddlMonths.SelectedValue),
        int.Parse(ddlDays.SelectedValue),
        int.Parse(ddlNumSCs.SelectedValue),
        txtDateFrom.Text,
        txtDateTo.Text,
        tradeGrades,
        executionGrades,
        int.Parse(txtMinProfitPips.Text),
        int.Parse(txtMaxProfitPips.Text));

    chtMyRepeatedChart.DataBind();
}

所以,我想替换DB.MyRepeatedCallchtMyRepeatedChart并将它们作为参数传递给一个通用函数。这可能吗?我的表单上有许多图表,它们都需要相同数量的参数。

谢谢

更新 根据Frederik的解决方案,我已经完成了以下操作:

private delegate IEnumerable<DataTable> GetDataSource(
    int TradeType,
    string RepeatedTrades,
    string Side,
    string TradeSetup,
    DataTable symbols,
    string ChartTimeFrame,
    int Hour,
    int Year,
    int Month,
    int Day,
    int NumSCs,
    string DateFrom,
    string DateTo,
    DataTable TradeGrades,
    DataTable ExecutionGrades,
    int MinProfitPips,
    int MaxProfitPips);

private void PopulateControl(Chart chart, GetDataSource getDataSource)
{
    //DBUtil DB = new DBUtil();
    DataTable symbols = GetSelectedItems("symbol", listSymbols);
    DataTable tradeGrades = GetSelectedItems("tradeGrade", listTradeGrades);
    DataTable executionGrades = GetSelectedItems("executionGrade", listExecutionGrades);

    chart.DataSource = getDataSource(
        int.Parse(ddlTradeTypes.SelectedValue),
        ddlRepeatedTrades.SelectedValue,
        radSide.SelectedValue,
        ddlTradeSetups.SelectedValue,
        symbols,
        ddlChartTimeFrames.SelectedValue,
        int.Parse(ddlHours.SelectedValue),
        int.Parse(ddlYears.SelectedValue),
        int.Parse(ddlMonths.SelectedValue),
        int.Parse(ddlDays.SelectedValue),
        int.Parse(ddlNumSCs.SelectedValue),
        txtDateFrom.Text,
        txtDateTo.Text,
        tradeGrades,
        executionGrades,
        int.Parse(txtMinProfitPips.Text),
        int.Parse(txtMaxProfitPips.Text));

    chart.DataBind();        
}       

我正在使用以下命令调用该函数:
PopulateControl (chtEquityCurve, DB.GetAccountBalances());

我在Intellisense中看到了这个错误: GetAccountBalances方法没有重载可以不传参数。


考虑使用表达式树,即使这不是一个简单的解决方案。 - Jahan Zinedine
4个回答

6

首先,创建一个委托类型(通常我建议人们使用可用的Func委托之一,但它们最多支持16个输入参数,而你有18个)。给它一个合适的名称,并定义所有输入参数,使它们具有正确的类型和描述性名称。使委托返回一个IEnumerable<T>

public delegate IEnumerable<WhateverTypeIsReturned> GetDataSource(int firstParam, [...]);

然后,修改PopulateMyRepeatedControl方法,使其如下所示:

private void PopulateMyRepeatedControl(Chart chart, GetDataSource getDataSource)
{
    DBUtil DB = new DBUtil();
    DataTable symbols = GetSelectedSymbols();
    DataTable tradeGrades = GetSelectedTradeGrades();
    DataTable executionGrades = GetSelectedExecutionGrades();        

    chart.DataSource = getDataSource (
        int.Parse(txtStartBalance.Text),
        int.Parse(ddlTradeTypes.SelectedValue),
        ddlRepeatedTrades.SelectedValue,
        radSide.SelectedValue,
        ddlTradeSetups.SelectedValue,
        symbols,
        ddlChartTimeFrames.SelectedValue,
        int.Parse(ddlHours.SelectedValue),
        int.Parse(ddlYears.SelectedValue),
        int.Parse(ddlMonths.SelectedValue),
        int.Parse(ddlDays.SelectedValue),
        int.Parse(ddlNumSCs.SelectedValue),
        txtDateFrom.Text,
        txtDateTo.Text,
        tradeGrades,
        executionGrades,
        int.Parse(txtMinProfitPips.Text),
        int.Parse(txtMaxProfitPips.Text));

    chart.DataBind();
}

当您调用该方法时,只需传递图表和用于收集数据的方法即可:
PopulateMyRepeatedControl(oneChart, OneDataCollectionMethod);
PopulateMyRepeatedControl(anotherChart, AnotherDataCollectionMethod);

当然,TheDataCollectionMethod必须具有正确的签名,否则代码将无法编译。
更新
关于您的更新,请注意您要将方法作为参数传递,而不是调用它:
PopulateControl (chtEquityCurve, DB.GetAccountBalances);

请注意,在方法名后没有括号。

为什么如果我使用Func委托作为List<List<T>>的单个参数? - TalentTuner
@Saurabh:那肯定可以,但它会少一些类型安全。另一个选项是创建一个类型,其中包含用于保存各种输入值的属性(我们称之为ChartDataSettings),并使用Func<ChartDataSettings> - Fredrik Mörk
@Fredrik:谢谢,我已经删除了括号,但现在出现了这个错误:参数2:无法将'method group'转换为'ASP.summarystats_aspx.GetDataSource' - Mark Allison
好的,谢谢。我有两个问题:在这个表单上,“GetAccountBalances”方法比其他所有方法多一个参数,而“GetAccountBalances”中的其他参数完全相同。我还想使用相同的代码来填充“DetailsView”控件以及所有的“图表”控件。感谢你迄今为止的帮助,我已经快要解决了... - Mark Allison
@Fredirik: 哦好的。我已经通过委托和将PopulateControl中的 Chart对象更改为 CompositeDataBoundControl 来填充我的DetailsView控件。但是,当我尝试填充一个 Chart时,它会出现此错误:Argument 1: cannot convert from 'System.Web.UI.DataVisualization.Charting.Chart' to 'System.Web.UI.WebControls.CompositeDataBoundControl' 有没有办法使用相同的 PopulateControl函数来填充两个 Chart控件和 DetailsView控件?再次感谢您的帮助! - Mark Allison
显示剩余7条评论

1

我认为你应该考虑使用一些面向对象编程的概念,而不是将方法名参数化。因为你有一个接受实际上是控件的参数的方法,所以考虑将其分离到不同的类中。

首先,你应该考虑重构代码,因为单个方法有太多的参数。你可以将所有参数封装到一个类中,例如

public class RepeatedCallParameters
{
   public string StartBalance{get;set;}

   ...

   ...
}

然后,您可以使用所有这些参数和一个名为PrepareRepeatedCallParameters()的方法创建一个抽象类。

public abstract class ChartProperties : Page
{
    protected abstract int StartBalance {get;}

    public RepeatedCallParameters PrepareRepeatedCallParameters()
    {
         RepeatedCallParameters p = new RepeatedCallParamters();
         p.StartBalance = StartBalance;
         return p;  
    }
}

然后

您可以在页面中实现此类

public class YourPage: ChartProperties
{
   protected override int StartBalance
   {
     get {return int.Parse(txtStartBalance.Text);} 
   }

   //All properties
   ..

   private void BindChartData()
   {
      RepeatedCallParameter p  = PrepareRepeatedCallParameters();
      Chart.DataSource = DB.RepeatedCall(p);
      Chart.DataBind();   
   }
}

这样,您就可以在抽象类中将参数填充的代码放在一个地方。

你可以这样做,Page继承自PageWithStatus,然后PageWithStatus再继承ChartProperties。 - dhinesh
@Mark Allsion:您是否需要在所有页面中都使用状态和图表属性?如果两者都需要,则可以将这两个类合并。 - dhinesh
是的,抽象类中所有属性都需要是抽象的,只有方法需要被实现。我也会编辑答案。 - dhinesh
@dhinesh:谢谢,PageWithStatus 是一个具体类。 - Mark Allison
@dhinesh:在我的图表页面中,它不喜欢受保护属性中的返回语句。即这一行:return int.Parse(txtStartBalance.Text);。我收到了这个错误:需要get或set访问器。你知道为什么吗?谢谢。 - Mark Allison
显示剩余7条评论

1

类似下面这样的内容

PopulateMyRepeatedControl(Chart c , Func<List<List<T>>>  actionToBeCalled)  

0

考虑使用委托,它可以被视为类型安全的函数指针。委托由委托类型和委托实例组成。我建议使用内置的泛型委托类型Action和Func。Action没有返回类型,而Func有。

public void MyMethod(Func<int, bool> predicate)
{
  if (predicate(5))
  {
    // true
  }
  else
  {
    // false
  }
}

// To call MyMethod, pass a delegate instance (here we are using a lambda expression)
MyMethod(x => x > 5);

委托实例从MyMethod中被调用(作为谓词参数),由于使用的逻辑,如果值为5,则返回false,否则返回大于5的任何值,返回true。您可以看到谓词的逻辑与MyMethod方法解耦,这使得MyMethod可重用于任何逻辑片段。
请注意,Func中使用的最后一个泛型类型是返回类型,Action没有返回类型。两种委托类型最多可以接受16个参数,但如果您需要这么多参数,您可能需要考虑您的设计!

只是一个小细节:您可以将此调用缩短为 MyMethod(x => x > 5);,它是等价的。 - Matthias Meid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接