如何将类的属性作为方法的参数传递?

发布于 2024-07-27 18:13:56 字数 1996 浏览 1 评论 0原文

我有一个类,其中有十几个代表不同金融领域的属性。 我有另一个类需要分别对每个字段执行一些计算。 除了进行计算的字段之外,这些计算方法中的代码是相同的。

有没有一种方法可以将属性名称作为参数传递,并且只用一个方法来完成所有执行工作,而不是为每个属性使用 12 个方法?

另外,我确信这可以通过反射来完成,但我在其他代码中看到过以同样的方式使用 lambda,并且想知道这是否是可以使用的候选者。

根据要求,这里是一个示例:

public class FinancialInfo
{
    public virtual DateTime AuditDate { get; set; }
    public virtual decimal ReleasedFederalAmount { get; set; }
    public virtual decimal ReleasedNonFederalAmount { get; set; }
    public virtual decimal ReleasedStateAmount { get; set; }
    public virtual decimal ReleasedLocalAmount { get; set; }
    public virtual decimal ReleasedPrivateAmount { get; set; }
    // more fields like this
}

public class FinancialLedger()
{
    public virtual DateTime? BeginDate { get; set; }
    public virtual DateTime? EndDate { get; set; }
    public virtual IList<FinancialInfo> Financials { get; set; } //not actual implementation, but you get the idea
    public decimal GetTotalReleasedFederalAmountByDate()
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            if (someCondition)
                if (someSubCondition)
                    total += fi.ReleasedFederalAmount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += fi.ReleasedFederalAmount;
            else if (anotherCondigion)
                total += fi.ReleasedFederalAmount;
        }
        return total;
    }
    public decimal GetTotalReleasedNonFederalAmountByDate()
    {
        // same logic as above method, 
        // but it accesses fi.ReleasedNonFederalAmount;
    }
    // More methods the same as the previous, just accessing different
    // members of FinancialInfo
}

我的目标是仅创建一个名为 GetTotalAmountByDate() 的方法,并传入开始日期、结束日期以及它需要访问的属性名称(ReleasedFederalAmount 或 ReleasedLocalAmount 等)。

我希望这准确地描述了我想要实现的目标。

I have a class that has a dozen or so properties that represent various financial fields. I have another class that needs to perform some calculations on each of those fields separately. The code inside those calculation methods are identical except for the field that it does the calculation on.

Is there a way that I can pass a property name as a parameter and just have one method that does all of the performing work instead of the 12 methods for each property?

Also, I'm sure this can be accomplished via reflection, but I've seen in other code where lambdas are used in this same kind of fashion and was wondering if this is a candidate where this can be used.

As requested, here is an example:

public class FinancialInfo
{
    public virtual DateTime AuditDate { get; set; }
    public virtual decimal ReleasedFederalAmount { get; set; }
    public virtual decimal ReleasedNonFederalAmount { get; set; }
    public virtual decimal ReleasedStateAmount { get; set; }
    public virtual decimal ReleasedLocalAmount { get; set; }
    public virtual decimal ReleasedPrivateAmount { get; set; }
    // more fields like this
}

public class FinancialLedger()
{
    public virtual DateTime? BeginDate { get; set; }
    public virtual DateTime? EndDate { get; set; }
    public virtual IList<FinancialInfo> Financials { get; set; } //not actual implementation, but you get the idea
    public decimal GetTotalReleasedFederalAmountByDate()
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            if (someCondition)
                if (someSubCondition)
                    total += fi.ReleasedFederalAmount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += fi.ReleasedFederalAmount;
            else if (anotherCondigion)
                total += fi.ReleasedFederalAmount;
        }
        return total;
    }
    public decimal GetTotalReleasedNonFederalAmountByDate()
    {
        // same logic as above method, 
        // but it accesses fi.ReleasedNonFederalAmount;
    }
    // More methods the same as the previous, just accessing different
    // members of FinancialInfo
}

My goal is to just make one method called GetTotalAmountByDate() and pass in a begin date, and end date and the name of the property (ReleasedFederalAmount or ReleasedLocalAmount, etc.) it needs to access.

I hope this depicts accurately what I'm trying to accomplish.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

愿得七秒忆 2024-08-03 18:13:56

如果您的属性都是数字并且可以被同质地视为单一类型(假设是十进制),则不需要反射。

像这样的事情应该可以解决问题:

protected decimal ComputeFinancialSum( DateTime? beginDate, DateTime? endDate,
                                       Func<FinancialInfo,decimal> propertyToSum )
{
    if (beginDate == null && endDate == null)
        return 0;
    decimal total = 0;
    foreach (var fi in Financials)
    {
        if (someCondition)
            if (someSubCondition)
                total += propertyToSum(fi);
        else if (someOtherCondition)
            if (someOtherSubCondition)
                total += propertyToSum(fi);
        else if (anotherCondigion)
            total += propertyToSum(fi);
    }
    return total;
}

然后您可以为所有特定情况提供适当命名的版本:

public decimal GetTotalReleasedFederalAmountByDate()
{
    return ComputeFinancialSum( BeginDate, EndDate, 
                                (x) => x.ReleasedFederalAmount );
}

public decimal GetTotalReleasedNonFederalAmountByDate()
{
    return ComputeFinancialSum( BeginDate, EndDate, 
                                (x) => x.ReleasedNonFederalAmount );
}

// other versions ....

You don't need reflection if your properties are all numeric and can be homogeneously treated as a single type - let's say a decimal.

Something like this should do the trick:

protected decimal ComputeFinancialSum( DateTime? beginDate, DateTime? endDate,
                                       Func<FinancialInfo,decimal> propertyToSum )
{
    if (beginDate == null && endDate == null)
        return 0;
    decimal total = 0;
    foreach (var fi in Financials)
    {
        if (someCondition)
            if (someSubCondition)
                total += propertyToSum(fi);
        else if (someOtherCondition)
            if (someOtherSubCondition)
                total += propertyToSum(fi);
        else if (anotherCondigion)
            total += propertyToSum(fi);
    }
    return total;
}

You can then provide appropriately named versions for all of your specific cases:

public decimal GetTotalReleasedFederalAmountByDate()
{
    return ComputeFinancialSum( BeginDate, EndDate, 
                                (x) => x.ReleasedFederalAmount );
}

public decimal GetTotalReleasedNonFederalAmountByDate()
{
    return ComputeFinancialSum( BeginDate, EndDate, 
                                (x) => x.ReleasedNonFederalAmount );
}

// other versions ....
悲凉≈ 2024-08-03 18:13:56

如果您的属性不是同一类型(假设您需要使用 int 和 long 属性作为参数)怎么办?

根据这个答案,您可以通过创建通用方法来改进它
https://stackoverflow.com/a/1179210/1461862

protected decimal ComputeFinancialSum<T>( DateTime? beginDate, DateTime? endDate,
                                       Func<FinancialInfo,T> propertyToSum )
{
    if (beginDate == null && endDate == null)
        return 0;
    decimal total = 0;
    foreach (var fi in Financials)
    {
        if (someCondition)
            if (someSubCondition)
                total += propertyToSum(fi);
        else if (someOtherCondition)
            if (someOtherSubCondition)
                total += propertyToSum(fi);
        else if (anotherCondigion)
            total += propertyToSum(fi);
    }
    return total;
}

然后你可以传递一个映射到不同字段的函数 不同的类型

public decimal GetTotalReleasedFederalAmountByDate()
{
    return ComputeFinancialSum<int>( BeginDate, EndDate, 
                                (x) => x.ReleasedFederalAmountInteger );
}

public decimal GetTotalReleasedNonFederalAmountByDate()
{
    return ComputeFinancialSum<long>( BeginDate, EndDate, 
                                (x) => x.ReleasedNonFederalAmountLong );
}

我从答案中获取了

IEnumerable.Where(fieldToUse== null).OrderDescendingBy(fieldToUse)

,然后使用原始代码来展示一种方法,但我实际上使用它来使用 linq 表达式,例如根据任何属性类型过滤和排序集合

What if your properties are not the same type (let say you need to use and int and long property as parameter) ?

Based on this answer you could then improve it by creating a generic method
https://stackoverflow.com/a/1179210/1461862

protected decimal ComputeFinancialSum<T>( DateTime? beginDate, DateTime? endDate,
                                       Func<FinancialInfo,T> propertyToSum )
{
    if (beginDate == null && endDate == null)
        return 0;
    decimal total = 0;
    foreach (var fi in Financials)
    {
        if (someCondition)
            if (someSubCondition)
                total += propertyToSum(fi);
        else if (someOtherCondition)
            if (someOtherSubCondition)
                total += propertyToSum(fi);
        else if (anotherCondigion)
            total += propertyToSum(fi);
    }
    return total;
}

Then you can just pass a function that maps to different fields with different types

public decimal GetTotalReleasedFederalAmountByDate()
{
    return ComputeFinancialSum<int>( BeginDate, EndDate, 
                                (x) => x.ReleasedFederalAmountInteger );
}

public decimal GetTotalReleasedNonFederalAmountByDate()
{
    return ComputeFinancialSum<long>( BeginDate, EndDate, 
                                (x) => x.ReleasedNonFederalAmountLong );
}

I have taken then original code from the answer to show a way, but I have actually used this to use a linq expresion like

IEnumerable.Where(fieldToUse== null).OrderDescendingBy(fieldToUse)

To filter and order a collection based on any property type

耳钉梦 2024-08-03 18:13:56

除了 Jon Skeet 提出的基于兰巴的好建议之外,你还可以尝试这样的方法。 (当然,它可能会改变某些代码的工作方式。)

public class ValueHolder
{
  object Value;
}

public class Main
{
  private ValueHolder value1 = new ValueHolder();
  private ValueHolder value2 = new ValueHolder();

  public Value1 { get { return value1.Value; } set { value1.Value = value; } }
  public Value2 { get { return value2.Value; } set { value2.Value = value; } }

  public ValueHolder CalculateOne(ValueHolder holder ...)
  {
    // Whatever you need to calculate.
  }

  public CalculateBoth()
  {
    var answer1 = CalculateOne(value1);
    var answer2 = CalculateOne(value2);
    ...
  }
}

Apart from the good lamba-based suggestion from Jon Skeet, you could try something like this. (Of course, it could change the way some of your code works.)

public class ValueHolder
{
  object Value;
}

public class Main
{
  private ValueHolder value1 = new ValueHolder();
  private ValueHolder value2 = new ValueHolder();

  public Value1 { get { return value1.Value; } set { value1.Value = value; } }
  public Value2 { get { return value2.Value; } set { value2.Value = value; } }

  public ValueHolder CalculateOne(ValueHolder holder ...)
  {
    // Whatever you need to calculate.
  }

  public CalculateBoth()
  {
    var answer1 = CalculateOne(value1);
    var answer2 = CalculateOne(value2);
    ...
  }
}
记忆で 2024-08-03 18:13:56

这可能是这里技术含量最低的答案,但为什么不只使用开关并合并多个“GetTotal...Amount”函数呢?

 // define some enum for your callers to use
 public enum AmountTypeEnum {
     ReleasedFederal = 1
 ,   ReleasedLocal = 2
 }

 public decimal GetTotalAmountByDate(AmountTypeEnum type)
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            // declare a variable that will hold the amount:
            decimal amount = 0;

            // here's the switch:
            switch(type) {
                case AmountTypeEnum.ReleasedFederal: 
                     amount = fi.ReleasedFederalAmount; break;
                case AmountTypeEnum.ReleasedLocal:
                     amount = fi.ReleasedLocalAmount; break;
                default: break;
            }

            // continue with your processing:
            if (someCondition)
                if (someSubCondition)
                    total += amount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += amount;
            else if (anotherCondigion)
                total += amount;
        }
        return total;
    }

这看起来更安全,因为所有逻辑都在您的控制之下(没有人向您传递函数来执行)。

如果您实际上需要以不同的量执行不同的操作,则可以进一步细分:

将处理部分转换为函数:

      private decimal ProcessNormal(decimal amount) {
           decimal total = 0;

           // continue with your processing:
            if (someCondition)
                if (someSubCondition)
                    total += amount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += amount;
            else if (anotherCondition)
                total += amount;
          return total;
     }

 public decimal GetTotalAmountByDate(AmountTypeEnum type)
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            // declare a variable that will hold the amount:
            decimal amount = 0;

            // here's the switch:
            switch(type) {
                case AmountTypeEnum.ReleasedFederal: 
                     amount = fi.ReleasedFederalAmount; 
                     total = ProcessNormal(amount);
                     break;
                case AmountTypeEnum.ReleasedLocal: 
                     amount = fi.ReleasedLocalAmount; 
                     total = ProcessNormal(amount);
                     break;
                case AmountTypeEnum.NonReleasedOtherAmount:
                     amount = fi.NonReleasedOtherAmount; 
                     total = ProcessSlightlyDifferently(amount);  // for argument's sake
                     break;
                default: break;
            }
        }
        return total;
    }

This is probably the lowest-tech answer here, but why not just use a switch and merge the multiple "GetTotal...Amount" functions?

 // define some enum for your callers to use
 public enum AmountTypeEnum {
     ReleasedFederal = 1
 ,   ReleasedLocal = 2
 }

 public decimal GetTotalAmountByDate(AmountTypeEnum type)
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            // declare a variable that will hold the amount:
            decimal amount = 0;

            // here's the switch:
            switch(type) {
                case AmountTypeEnum.ReleasedFederal: 
                     amount = fi.ReleasedFederalAmount; break;
                case AmountTypeEnum.ReleasedLocal:
                     amount = fi.ReleasedLocalAmount; break;
                default: break;
            }

            // continue with your processing:
            if (someCondition)
                if (someSubCondition)
                    total += amount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += amount;
            else if (anotherCondigion)
                total += amount;
        }
        return total;
    }

This seems safer, because all your logic remains under your control (no one is passing you functions to execute).

This can be further broken down if you need to actually do different things with different amounts:

Take the processing part and turn it into a function:

      private decimal ProcessNormal(decimal amount) {
           decimal total = 0;

           // continue with your processing:
            if (someCondition)
                if (someSubCondition)
                    total += amount;
            else if (someOtherCondition)
                if (someOtherSubCondition)
                    total += amount;
            else if (anotherCondition)
                total += amount;
          return total;
     }

 public decimal GetTotalAmountByDate(AmountTypeEnum type)
    {
        if (BeginDate == null && EndDate == null)
            return 0;
        decimal total = 0;
        foreach (var fi in Financials)
        {
            // declare a variable that will hold the amount:
            decimal amount = 0;

            // here's the switch:
            switch(type) {
                case AmountTypeEnum.ReleasedFederal: 
                     amount = fi.ReleasedFederalAmount; 
                     total = ProcessNormal(amount);
                     break;
                case AmountTypeEnum.ReleasedLocal: 
                     amount = fi.ReleasedLocalAmount; 
                     total = ProcessNormal(amount);
                     break;
                case AmountTypeEnum.NonReleasedOtherAmount:
                     amount = fi.NonReleasedOtherAmount; 
                     total = ProcessSlightlyDifferently(amount);  // for argument's sake
                     break;
                default: break;
            }
        }
        return total;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文