避免多个类似的foreach - C#

时间:2014-12-11 17:12:02

标签: c# foreach

如果我在错误的社区发帖,我很抱歉,我在这里很新。

我有多个方法使用相同的foreach循环,只更改我调用的内部方法:

    public void CalculationMethod1()
    {
        foreach (Order order in ordersList)
        {
            foreach (Detail obj_detail in order.Details)
            {
                CalculateDiscount(obj_detail);
            }
        }
    }

    public void CalculationMethod2()
    {
        foreach (Order order in ordersList)
        {
            foreach (Detail obj_detail in order.Details)
            {
                CalculateTax(obj_detail);
            }
        }
    }

每个内部方法都有不同的逻辑,数据库搜索,数学计算(这里不重要)。

我想调用上面的方法,而不是每次都重复foreach循环,所以我将通过以下解决方案:

    public void CalculateMethod_3()
    {
        foreach (Order obj_order in ordersList)
        {
            foreach (Detail obj_detail in order.Details)
            {
                CalculateDiscount(obj_detail);
                CalculateTax(obj_detail);
            }
        }
    }

但我陷入了规则问题:

 class Program
 {
    static void Main(string[] args)
    {
        Calculation c = new Calculation();
        c.CalculateMethod_3();
        c.AnotherMethod_4(); //It doesn't use objDetail
        c.AnotherMethod_5(); //It doesn't use objDetail
        c.CalculateMethod_6(); //Method 6 needs objDetail but respecting the order of the methods, so It must be after AnotherMethod_4 and AnotherMethod_5
    }
 }

如何创建一种方法来实现我的目标(我不想重复代码)尊重上述规则?

5 个答案:

答案 0 :(得分:8)

您总是可以将委托传递给该方法,然后您可以基本上做任何您想做的事情。

public void ApplyToDetails(Action<Detail> callback)
{
    foreach (Order order in ordersList)
    {
        foreach (Detail obj_detail in order.Details)
        {
            callback(obj_detail);
        }
    }       
}

然后使用你做这样的事情

ApplyToDetails(detail => CalculateTax(detail));
ApplyToDetails(detail =>
{
    CalculateDiscount(detail);
    CalculateTax(detail);
});

答案 1 :(得分:1)

代表们在很多情况下非常方便,在这种情况下肯定是这样。我知道这已经得到了回答并且是正确的,但这里有一个比较的替代方案。我提供了一个链接,为您提供一些见解。

public class CalculationMethods
{
    public delegate void CalculationDelegate(Detail method);

    private Dictionary<string, CalculationDelegate> _methods;

    public CalculationMethods
    {
        this._methods = new Dictionary<string, CalculationDelegate>()
        {
            { "Discount", CalculateDiscount },
            { "Tax",      CalculateTax      }
        };
    }

    public void Calculate(string method, Detail obj_detail)
    {
        foreach (Order order in ordersList)
        {
            foreach (Detail obj_detail in order.Details)
            {
                var m = this._methods.FirstOrDefault(item => item.Key == method).Value;
                m(obj_detail);
            }
        }
    }
}

<强>用法:

//Initialize
var methods = new CalculationMethods();

//Calculate Discount
methods.Calculate("Discount", obj_detail);

//Calculate Tax
methods.Calculate("Tax", obj_detail);

旁注: 如果在代表列表中找不到计算方法,我会建议一些异常处理。示例如下:(用以下内容替换calculate方法。)

public void Calculate(string method, Detail obj_detail)
{
    foreach (Order order in ordersList)
    {
        foreach (Detail obj_detail in order.Details)
        {
            var m = this._methods.FirstOrDefault(item => item.Key == method).Value;

            //Check if the method was found
            if (m == null)
                throw new ApplicationNullException("CalculationDelegate")

            m(obj_detail);
        }
    }
}

体面教程: Delegates and Events

答案 2 :(得分:0)

您可以使用代理人。 (谷歌它 - 我面前没有开发环境为你准备样品)。基本上是一个让委托调用的方法: 这是伪代码......

public void CalculationMethod(delegate myFunction) // need to look up correct param syntax
{
    foreach (Order order in ordersList)
    {
        foreach (Detail obj_detail in order.Details)
        {
            myFunction(); // Need to lookup correct calling syntax
        }
    }
}

我用Google搜索“c#delegate as parameter”并提出了http://msdn.microsoft.com/en-us/library/ms173172.aspx,这似乎是一个合理的解释。

答案 3 :(得分:0)

正如Darren Kopp所说,你可以使用代表。但是,如果您使用参数调用方法,则可以直接调用它(您不需要lambda表达式)。

使用public void ApplyToDetails(Action<Detail> callback) { ... }

ApplyToDetails(Method_1);         // Uses objDetail
ApplyToDetails(d => Method_2());  // Doesn't use objDetail
ApplyToDetails(d => Method_3());  // Doesn't use objDetail
ApplyToDetails(Method_4);         // Uses objDetail

请注意,您不能在作为委托传递的方法之后放置参数括号!

答案 4 :(得分:-1)

您可以像其他答案提供的那样使用代理,但我相信您这样做会导致代码过于混乱。如果在每种方法中重新声明foreach循环,则代码更清晰,更易读。只有当你复制粘贴内部部分时,我才会说你冒着代码重复的风险。

以这种方式思考:如果您创建了一个传递委托的方法,那么该方法的名称会被调用么?这是一种方法,可以为您传入的每个订单中的每个详细信息执行某些操作,并且应该命名为DoSomethingForEachDetailInOrders()。这种方法适用于哪种类?你不知道你在委托中实际做了什么,所以这个类的目的必须是更多的框架式代码,你的应用程序似乎不够复杂,无法保证。

此外,如果您正在调试此代码或通过它阅读,而不是能够在您正在阅读的方法中看到2个foreach循环,您必须滚动到委托的定义,阅读它,然后去回到你的方法并继续阅读。

编辑:我最初通过淡化foreach循环的重复来回答这个问题,希望OP不会给他的应用程序增加额外的复杂性,试图让它遵循“最佳实践”。我没有深入研究,因为代码需要更具侵入性的可维护性重构。 foreach循环代码气味源于其他问题,详见本答案下方的评论。我仍然坚持认为添加委托方法比重复循环更不可取,因为委托方法选项几乎是教科书样板。

添加了一个代码示例来解释如果可维护性是一个问题,应该如何重构代码:

public decimal CalculateDiscount(IEnumerable<Order> ordersList)
{
    return ordersList.SelectMany(order => order.Details).Sum(detail => detail.Discount);
}

public decimal CalculateTax(IEnumerable<Order> ordersList)
{
    return ordersList.SelectMany(order => order.Details).Sum(detail => detail.Total) * taxRate;
}

如果你绝对必须有一个自定义函数来获取订单的所有细节(可以重构为扩展方法):

public IEnumerable<Detail> GetDetailsForOrders(IEnumerable<Orders> orderList)
{
    foreach(var order in orderList)
    {
        foreach (var detail in order.Details)
        {
            yield return detail;
        }
    }
}

public decimal CalculateDiscount(IEnumerable<Order> ordersList)
{
    return GetDetailsForOrders(ordersList).Sum(detail => detail.Discount);
}

public decimal CalculateTax(IEnumerable<Order> ordersList)
{
    return GetDetailsForOrders(ordersList).Sum(detail => detail.Total) * taxRate;
}