2009-07-24 9 views
5

Tengo una clase que tiene una docena o más de propiedades que representan varios campos financieros. Tengo otra clase que necesita realizar algunos cálculos en cada uno de esos campos por separado. El código dentro de esos métodos de cálculo es idéntico, excepto por el campo en el que realiza el cálculo.¿Cómo puedo pasar una propiedad de una clase como parámetro de un método?

¿Hay alguna manera de que pueda pasar un nombre de propiedad como parámetro y tener solo un método que haga todo el trabajo de interpretación en lugar de los 12 métodos para cada propiedad?

Además, estoy seguro de que esto se puede lograr a través de la reflexión, pero he visto en otro código que las lambdas se usan de la misma manera y me pregunto si este es un candidato donde se puede usar.

Conforme a lo solicitado, aquí es un ejemplo:

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 
} 

Mi objetivo es simplemente hacer un método llamado GetTotalAmountByDate() y pasar de una fecha de inicio y fecha de finalización y el nombre de la propiedad (ReleasedFederalAmount o ReleasedLocalAmount, etc.) necesita acceder.

Espero que esto represente con precisión lo que estoy tratando de lograr.

+0

Será más fácil de responder si se puede compartir una ejemplo de tal función, y una idea de cómo se verían las propiedades de la clase. –

+2

Tengo curiosidad por saber por qué su método de cálculo no puede tomar, por ejemplo, la instancia de la clase de campos financieros, y un valor (es decir, el valor de la propiedad sobre la que desea realizar el cálculo) como parámetros. ¿Puedes publicar algún código auxiliar para iluminar tu pregunta? –

+0

use el mismo tipo en la definación de su función, que utilizó en la propiedad de la clase. –

Respuesta

5

No es necesario reflexión si sus propiedades son todas numéricas y pueden tratarse homogéneamente como un solo tipo, digamos un decimal.

Algo como esto debe hacer el truco:

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; 
} 

A continuación, puede proporcionar versiones con nombres correspondientes para todos sus casos específicos:

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

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

// other versions .... 
+0

Esto es precisamente lo que estaba buscando, ¡gracias! –

0

Además de la buena sugerencia basada en lamba de Jon Skeet, podrías intentar algo como esto. (Por supuesto, podría cambiar la forma en que algunos de su código funciona.)

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); 
    ... 
    } 
} 
0

Esta es probablemente la respuesta más baja tecnología aquí, pero ¿por qué no sólo tiene que utilizar un interruptor y fusionar las múltiples "getTotal ... Cantidad "¿funciones?

// 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; 
    } 

Esto parece más seguro, porque toda su lógica permanece bajo su control (nadie te está pasando para ejecutar funciones).

Esto puede subdividirse si deberías hacer cosas diferentes con diferentes cantidades:

Tome la parte de procesamiento y convertirlo en una función:

 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; 
    } 
Cuestiones relacionadas