C#:如何在嵌套循环中使用委托处理实例方法以消除代码冗余
解决DogWalker类代码冗余的委托重构方案
场景与原始代码
一位遛狗员拥有多位客户,每位客户饲养一只或多只狗,对应场景的C#原始代码如下:
public class Dog { public int NeedForWalk { get; set; } public int NeedForFood { get; set; } public Dog() { } public void TakeForWalk(int walkTime) { // 遛狗会相应降低狗狗的遛弯需求 NeedForWalk -= walkTime; } public void GiveFood(int amountOfFood) { // 喂食会相应降低狗狗的进食需求 NeedForFood -= amountOfFood; } } public class Customer { public IEnumerable<Dog> Dogs { get; set; } public Customer() { } } public class DogWalker { public IEnumerable<Customer> Customers { get; set; } public DogWalker() { } public void walkDogs(int walkTime) { foreach (Customer customer in Customers) { foreach (Dog dog in customer.Dogs) { dog.TakeForWalk(walkTime); } } } public void feedDogs(int amountOfFood) { foreach (Customer customer in Customers) { foreach (Dog dog in customer.Dogs) { dog.GiveFood(amountOfFood); } } } }
重构需求与问题
可以看到DogWalker类的walkDogs()和feedDogs()方法逻辑高度重复,均是遍历所有客户的狗狗并执行对应操作。希望通过辅助方法+委托消除代码冗余,但遇到两个问题:
- 如何正确实现
applyToDogs(),使其支持Func<int>以外的委托签名; - 如何在
walkDogs()和feedDogs()方法中调用applyToDogs()。
解决方案
方案1:使用闭包的简洁实现
通过Action<Dog>委托接收对单个狗狗的操作,调用方利用lambda表达式绑定参数,实现类型安全且简洁的重构:
public class DogWalker { public IEnumerable<Customer> Customers { get; set; } public DogWalker() { } public void walkDogs(int walkTime) { applyToDogs(dog => dog.TakeForWalk(walkTime)); } public void feedDogs(int amountOfFood) { applyToDogs(dog => dog.GiveFood(amountOfFood)); } #region 辅助方法 private void applyToDogs(Action<Dog> action) { foreach (Customer customer in Customers) { foreach (Dog dog in customer.Dogs) { action(dog); } } } #endregion }
说明:
- 辅助方法
applyToDogs只负责遍历所有狗狗,将每个狗狗实例传递给传入的Action<Dog>委托; - 在
walkDogs和feedDogs中,通过lambda表达式把狗狗的实例方法(如TakeForWalk)和当前方法的参数(如walkTime)绑定,生成符合Action<Dog>签名的委托; - 这种方式无需处理复杂的参数列表,代码简洁且类型安全,避免了装箱拆箱开销。
方案2:泛型参数的直观实现
如果希望明确传递操作参数,可以使用泛型版本的Action<Dog, T>委托,让参数传递更清晰:
public class DogWalker { public IEnumerable<Customer> Customers { get; set; } public DogWalker() { } public void walkDogs(int walkTime) { applyToDogs((dog, time) => dog.TakeForWalk(time), walkTime); } public void feedDogs(int amountOfFood) { applyToDogs((dog, food) => dog.GiveFood(food), amountOfFood); } #region 辅助方法 private void applyToDogs<T>(Action<Dog, T> action, T parameter) { foreach (Customer customer in Customers) { foreach (Dog dog in customer.Dogs) { action(dog, parameter); } } } #endregion }
说明:
- 泛型辅助方法
applyToDogs<T>接收两个参数:一个带狗狗实例和操作参数的委托,以及对应的操作参数; - 调用时直接传递lambda表达式和参数,逻辑更直观,适合需要明确展示参数传递关系的场景。
内容的提问来源于stack exchange,提问作者CJS
相关产品推荐
相关产品推荐

