将C#代码从内部foreach循环重构到外部foreach循环

将C#代码从内部foreach循环重构到外部foreach循环,c#,foreach,C#,Foreach,我的代码当前看起来像: foreach(var request in requestsList){ foreach(var myVar in request.anotherList){ if(myVar.hasPermissions()) { //do something } } } requestsList是请求的列表。 myVar.hasPermissions()需要连接到数据库,因此我希望尽量减少对数据库的调用次数。我想将

我的代码当前看起来像:

foreach(var request in requestsList){
    foreach(var myVar in request.anotherList){
       if(myVar.hasPermissions()) {
           //do something
       }
    }
}
requestsList
请求的
列表。
myVar.hasPermissions()
需要连接到数据库,因此我希望尽量减少对数据库的调用次数。我想将它移到内部
foreach
循环之外,并且每个请求只进行一次调用

我正在努力实现这样的目标:

foreach(var request in requestsList){
    //check for permissions
    boolean perm = myVar.hasPermissions(); //database call to check permissions
    foreach(var myVar in request.anotherList){
       if(perm) {
           //do something
       }
    }
}
我要做的就是将
hasphomissions()
移动到内部
foreach
循环的外部。
我面临的问题是,我无法访问外部
foreach
循环中的
myVar
。两个循环都在列表上迭代,这对我来说很困难。

我想你称之为顶部循环中的最小次数

如果你换一种方式考虑

//First get all the 'another' guys:
var allAnother = requestsList.SelectMany(anotherList => anotherList).ToList();

//If you don't have to check them all 
var permitGuys = allAnother.Distinct().Where(a => a.hasPermissions()).ToList();

//Do something with them
foreach(var permitGuy in permitGuys)
{
     //Do something
}

如果
hasPermission
是相对静态的,即您确信它不会在外部循环的运行中更改,则可以在检查权限时缓存权限:

IDictionary<MyVarType,bool> checked = new IDictionary<MyVarType,bool>();
foreach(var request in requestsList){
    foreach(var myVar in request.anotherList) {
        bool permitted;
        if (!checked.TryGetValue(myVar, out permitted)) {
            permitted = myVar.hasPermissions();
            checked.Add(myVar, permitted);
        }
        if(permitted) {
            //do something
        }
    }
}
IDictionary选中=新建IDictionary();
foreach(请求列表中的var请求){
foreach(请求中的var myVar.anotherList){
允许使用bool;
如果(!checked.TryGetValue(myVar,允许超出)){
允许=myVar.hasphopermissions();
选中。添加(myVar,允许);
}
如果(允许){
//做点什么
}
}
}

这样,您就可以对
myVar
的每个不同实例只调用一次
haspowpermissions
;所有后续检查都将来自
已检查的
缓存。

没有关于类的更多详细信息,我们只能推测如何最好地解决问题。假设您的
另一个列表
包含类似于用户列表的内容,您可以缓存检查结果,以便不再检查同一用户

您可以添加一个公共字段,该字段使用
Lazy
缓存调用
hasPermissions
的结果-您必须在构造函数中初始化它:

public class User {
    public bool hasPermissions() { // check database for permissions
        var ans = false; // ...
        return ans;
    }

    public Lazy<bool> cachedPermissions;

    public User() {
        UncachePermissions();
    }

    public void UncachePermissions() => cachedPermissions = new Lazy<bool>(() => hasPermissions());
}
每个
用户
对象只调用一次
hasPermissions
。如果单个数据库调用可能存在多个
User
对象,则需要更多关于类和方法的详细信息

我添加了
UncachePermissions
方法来重置缓存,否则您可能会使用非常旧的
haspmissions
值,这可能会导致问题。如果这可能是一个常见问题,您可以在对象外部缓存,作为循环的一部分:

var permissionCache = new Dictionary<User, bool>();

foreach (var request in requestsList) {
    foreach (var myVar in request.anotherList) {
        bool permission;
        if (!permissionCache.TryGetValue(myVar, out permission)) {
            permission = myVar.hasPermissions();
            permissionCache.Add(myVar, permission);
        }
        if (permission) {
            //do something
        }
    }
}
var permissionCache=new Dictionary();
foreach(请求列表中的var请求){
foreach(请求中的var myVar.anotherList){
布尔许可;
if(!permissionCache.TryGetValue(myVar,out权限)){
permission=myVar.haspmissions();
permissionCache.Add(myVar,permission);
}
如果(许可){
//做点什么
}
}
}

myVar
在循环之外不存在。它是随循环而变化的变量。因此,
hasPermissions()
的结果大概也会改变。因此,无论如何都需要再次调用它。如果该操作的结果不应该改变,那么您的建模是不正确的。停止思考欺骗循环的方法,开始思考对象做什么以及它们如何交互。从语义上讲,这些对象和操作对代码的执行意味着什么?我们必须假设
haspowpermissions
使用
myVar
中的值,这就是为什么它是类上的一个方法。如果是这样,您不能简单地将其移到外部,因为要检查多个值。
另一个列表
myVar
hasPermissions
的性质是什么?跳出循环不是一个解决方案。在代码中,您必须访问数据库n次。我建议更改函数的设计,对数据库进行一次访问(使用request.anotherList作为参数,例如使用sql IN子句),并将结果放入内存中,以便在内部循环中使用。您最终检查了多少“myVar”?仅使用“myVar.Id”和“myVar.hasPermissions”就可以完成此检查吗?如果是这样的话,您能否在这两个循环中的任何一个循环之前访问数据库一次,并获取所有myVar ID和hasPermissions值?然后,您只需与集合进行比较,而不必重复访问数据库。如果需要多次检查并且“hasPermissions”值不经常更改,也可以在缓存中使用此选项。在迭代对象之前,不能检查权限。如果您需要检查所有请求中所有列表项的所有权限,则没有更有效的方法来执行此操作。除非您输入
Distinct
,否则不会减少数据库调用。这实际上不会改变代码的行为,因为问题是请求的。(从技术上讲,你正在创建多个完全不必要的集合,消耗大量内存却毫无益处,所以说你没有做出任何有益的行为改变更为正确。)嘎,我忘记了代码甚至没有正确的结果的区别,因此,它的性能特征完全是不相关的……你不知道——我们在这里做了很多假设——我试图强调另一种思考问题的方式,将其分成更小的部分
var permissionCache = new Dictionary<User, bool>();

foreach (var request in requestsList) {
    foreach (var myVar in request.anotherList) {
        bool permission;
        if (!permissionCache.TryGetValue(myVar, out permission)) {
            permission = myVar.hasPermissions();
            permissionCache.Add(myVar, permission);
        }
        if (permission) {
            //do something
        }
    }
}