C# 避免使用多个类似的foreach-C

C# 避免使用多个类似的foreach-C,c#,foreach,C#,Foreach,如果我发错了帖子,我很抱歉,我是新来的 我有多个方法使用相同的foreach循环,仅更改我调用的内部方法: public void CalculationMethod1() { foreach (Order order in ordersList) { foreach (Detail obj_detail in order.Details) { CalculateDis

如果我发错了帖子,我很抱歉,我是新来的

我有多个方法使用相同的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
    }
 }

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

您可以使用委托。谷歌it——我面前没有一个开发环境可以为您运行一个示例。基本上有一种方法需要委托来调用: 这里是伪代码

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

我在谷歌上搜索了c delegate作为参数,得出了一个合理的解释。

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

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

您可以像其他答案所提供的那样使用委托,但我相信在您的情况下,这样做会导致代码过于混乱。如果您在每个方法中重新声明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;
}

正如Darren Kopp所说,您可以使用委托。但是,在使用参数调用方法的情况下,可以直接调用它,而不需要lambda表达式

使用public void ApplyToDetailsAction回调{…}:


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

学员在许多情况下都非常方便,而且在这种情况下肯定非常方便。我知道这一点已经得到了正确的回答,但这里有一个可供比较的替代方案。我提供了一个链接,让您了解一些情况

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);
            }
        }
    }
}
旁注: 我建议进行一些异常处理,以防在代理列表中找不到计算方法。下面的示例:将计算方法替换为以下方法

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);
        }
    }
}
体面教程:

我不明白您的解决方案有什么问题。这个解决方案的问题是CalculateMethod_6需要对象详细信息,但我不能再调用CalculateMethod_3,因为我不再需要CalculatedInsect或CalculateTax。实际上,我不能调用。所以CalculateMethod_6也有类似的foreach样式,你只想切换循环中的内容?@moarboilerplate基本上我不想为我创建的每个新方法编写foreach块,重复是不是一种不好的做法?我认为只是添加了一个答案,当然,你也可以调用无参数方法:ApplyToDetailsdetail=>AnotherMethod_4;谢谢你们,这确实是一条路要走!您可以在CalculateTax之后简化对ApplyToDetailsCalculateTax的调用,而不使用参数括号,因为CalculateTax已经对应于一个操作。建议不错!谢谢是的,它与提供的其他解决方案相匹配。谢谢想想更复杂的情况,例如,你必须遍历一棵二叉树。有一个ApplyToAllItems方法是有意义的,因为它提供了一个抽象。调用方不需要知道数据结构的详细信息。如果在福图
重新更改数据结构,这不会影响调用方代码,只是应用。。。方法。这正是我的想法……代码维护,这是因为代码重复与我有关。我上面提供的解决方案实现了一个委托字典。有了结构良好的语法和适当的文档使用,可读性应该不会受到影响。学员帮助提供了一种轻松扩展应用程序的途径。您的语法越通用和模块化,它就越可扩展。@OlivierJacot Descombes我同意您在示例的上下文中以及当传入对象的状态需要更改时的看法。在OP的代码中,我不同意。OP问题的根源在于他的代码是由内而外的。他正在调用一个void聚合方法来修改他的类的全局状态,这就是为什么他对函数调用的顺序有问题。您提到的抽象可以通过基本上重新实现SelectMany并在生成的集合中应用聚合函数来实现,但是按照建议使用委托实际上会加剧重复。@DaniloRuziska如果您担心维护,foreach循环的重复不应该是您的主要问题。您应该关心的是,您的方法是过程性的,而不是功能性的,这就是为什么会出现重复的foreach循环的代码味道。如果您改变调用函数的方式,从而可以调用CalculateTaxGetDetailsForDerders,那么代码的可维护性将提高100倍。
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);
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);
        }
    }
}