简体   繁体   English

避免多个相似的foreach-C#

[英]Avoid multiple similar foreach - C#

I apologize if I'm posting into the wrong community, I'm quite new here. 抱歉,如果我发布的社区错误,我在这里很陌生。

I have multiple methods using the same foreach loop, changing only the inner method I call: 我有多个方法使用相同的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);
            }
        }
    }

Each inner method has different logic, database search, math calculations (not important here). 每个内部方法都有不同的逻辑,数据库搜索,数学计算(此处不重要)。

I'd like to call the methods above without repeating the foreach loop everytime, so I throught about the solution below: 我想在不每次都重复foreach循环的情况下调用上述方法,因此我将介绍以下解决方案:

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

But I fall into a rule problem: 但是我陷入一个规则问题:

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

How can I create a method to achieve my objective (I don't want to repeat code) respecting the rule above? 我该如何创建一种方法来实现我的目标(我不想重复代码),并且遵守上述规则?

You can always pass a delegate to the method and then you can do basically whatever you want. 您总是可以将委托传递给该方法,然后您基本上可以执行所需的任何操作。

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

Then to use you'd do something like this 然后要使用,你会做这样的事情

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

Delegates come in very handy in many cases and definitely in such a case. 在很多情况下,而且在这种情况下,代表都非常方便。 I know this has already been answered and rightly so, but here is an alternative for comparison. 我知道这已经得到了答案,这是正确的,但这是比较的替代方法。 I have provided a link to give you some insight. 我提供了一个链接,可为您提供一些见识。

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

Usage: 用法:

//Initialize
var methods = new CalculationMethods();

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

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

Side Note: I would recommend some exception handling in case the method of calculation isn't found among the list of delegates. 旁注:如果在代理列表中找不到计算方法,我将建议一些异常处理。 Example below: (Replace the calculate method with the following.) 下面的示例:(将计算方法替换为以下内容。)

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

Decent tutorial: Delegates and Events 体面的教程: 代表和活动

You can use delegates. 您可以使用委托。 (Google it - I don't have a development environment in front of me to run up a sample for you). (谷歌搜索-我前面没有开发环境来为您创建示例)。 Basically one method that takes a delegate to call: Here is pseudo code... 基本上是一个需要委托调用的方法:这是伪代码...

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

I googled "c# delegate as parameter" and came up with http://msdn.microsoft.com/en-us/library/ms173172.aspx which seems to be a reasonable explanation. 我用Google搜索“ c#委托作为参数”,并提出了http://msdn.microsoft.com/en-us/library/ms173172.aspx ,这似乎是一个合理的解释。

As Darren Kopp says, you can use delegates. 正如Darren Kopp所说,您可以使用委托。 However, in the case that you are calling a method with a parameter, you can call it directly (you don't need the lambda expression). 但是,如果要使用参数调用方法,则可以直接调用它(不需要lambda表达式)。

With public void ApplyToDetails(Action<Detail> callback) { ... } : 使用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

Note that you must not place parameter braces after the methods you pass as delegate! 请注意,您不得在作为委托传递的方法之后放置参数括号!

You could use delegates as the other answers have provided but I believe in your case doing so will lead to overly confusing code. 您可以像其他答案一样使用委托,但我相信您的情况会导致代码过于混乱。 Your code is cleaner and more readable if you redeclare the foreach loops in each method. 如果在每种方法中重新声明foreach循环,则代码将更清晰,可读性更好。 Only if you were copy-pasting portions of the internals would I say you run the risk of code duplication. 仅当您复制内部部件的一部分时,我才说您冒着代码重复的风险。

Think about it this way: If you created a method passing in a delegate, what would the name of this method be called? 这样考虑:如果您创建了一个传递委托的方法,那么该方法的名称将叫什么? It is a method that does something for each Detail in each Order you pass in and should be named something like DoSomethingForEachDetailInOrders() . 它是一种对传入的每个Order中的每个Detail进行操作的方法,应命名为DoSomethingForEachDetailInOrders() What kind of class would this method exist for? 此方法将用于哪种类? You don't know what it is you're actually doing in the delegate, so the purpose of this class would have to be more framework-style code, which your app does not appear to be complex enough to warrant. 您不知道您在委托中实际上在做什么,因此此类的目的必须是采用更多框架样式的代码,而您的应用似乎不够复杂,无法保证。

Additionally, if you were debugging this code or reading through it, instead of being able to see 2 foreach loops inside the method you are reading, you have to go scroll to the definition of the delegate, read that, and then go back to your method and resume reading. 此外,如果您正在调试或阅读这段代码,而不是能够在正在读取的方法中看到2个foreach循环,则必须滚动到委托的定义,阅读该委托的定义,然后返回到方法并继续阅读。

Edit: I originally answered this question by downplaying the duplication of the foreach loops in the hopes that OP would not add additional complexity to his app attempting to make it follow "best practices." 编辑:我最初通过淡化foreach循环的重复性来回答这个问题,希望OP不会在尝试使其遵循“最佳实践”的应用程序中增加额外的复杂性。 I didn't go deeper because the code requires a more intrusive refactor for maintainability. 我并没有更深入,因为代码需要更具侵入性的重构才能实现可维护性。 The foreach loop code smell stems from other problems as detailed in the comments below this answer. foreach循环代码的气味源于其他问题,如该答案下方的注释中所述。 I still stand by my opinion that adding the delegate method is less desirable than the duplicated loops because the delegate method option is pretty much textbook boilerplate. 我仍然坚持认为,添加委托方法比重复循环更不可取,因为委托方法选项几乎是教科书的样板。

Added a code example to explain how the code should be refactored if maintainability is a concern: 添加了一个代码示例,以说明在考虑到可维护性的情况下应如何重构代码:

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

If you ABSOLUTELY MUST HAVE a custom function for getting all details for orders (could be refactored to an extension method): 如果您绝对必须有一个自定义函数来获取订单的所有详细信息(可以重构为扩展方法):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM