递归函数内的局部变量未分配

时间:2010-02-14 16:37:28

标签: c# recursion

我有以下代码,有关如何解决此问题的任何想法,而不是在函数外声明一个int变量?我得到以下编译器错误:使用未分配的局部变量'counter'

public static int GetNumberOfDevicesForManagementGroup(Guid managementGroupId, bool firstTime)
  {
     int counter;
     using (var ctx = new DeviceManagerEntities())
     {
        if (firstTime)
        {
           firstTime = false;
           counter = 0;
           GetNumberOfDevicesForManagementGroup(managementGroupId, firstTime);
        }
        else
        {
           var groups = ctx.ManagementGroups
              .Where(x => x.ParentId == managementGroupId)
              .ToList();
           if (groups.Count != 0)
           {
              foreach (ManagementGroups group in groups)
              {
                 var devices = ctx.Devices
                    .Where(x => x.ManagementGroups.ManagementGroupId == group.ManagementGroupId)
                    .ToList();
                 foreach (Devices device in devices)
                 {
                    counter++;
                 }
                 GetNumberOfDevicesForManagementGroup(group.ManagementGroupId, firstTime);
              }
           }
           else
           {
              var devices = ctx.Devices
                    .Where(x => x.ManagementGroups.ManagementGroupId == managementGroupId)
                    .ToList();
              foreach (Devices device in devices)
              {
                 counter++;
              }
           }
        }
     }
     return counter;
  }

5 个答案:

答案 0 :(得分:8)

这个功能似乎有很多东西错误。

  1. 您有一个递归函数,创建一个新的实体上下文 - 并在处理上下文之前进行递归!因此,这不仅会创建大量冗余的ObjectContext实例,而且它们都是同时使用。整个事情应该被完全重写,以便在函数调用之间共享一个上下文。

  2. 您在静态方法中创建ObjectContext。这真是糟糕的设计。特别是考虑到这个方法的名称,你可能会滥用静态方法来实现有效的过程代码。这应该是一个实例方法,该类应该是实际维护ObjectContext的东西。

  3. 你有很多这样的行:GetNumberOfDevicesForManagementGroup(managementGroupId, firstTime);。除了浪费CPU周期和数据库时间之外,它们什么都不做。你丢掉了从他们那里得到的结果。看起来您认为GetNumberOfDevicesForManagementGroup的连续执行将共享相同的counter变量;那不是递归的工作原理,那不是子程序的工作原理,而是使counter成为一个全局变量来补偿错误的错误

  4. 您只需下载所有“设备”并逐个计算,而不是在每个实例中实际获得计数。这又是对CPU和数据库时间的巨大浪费。

  5. 您正在循环中运行数据库查询。让人惊讶。

  6. 第一个firstTime = false;块中的两行counter = 0;if根本不执行任何操作。您正在分配函数参数。这些都是无操作。

  7. 您实际上从未对counter块初始化else,因此编译器错误确实不足为奇。如果你想增加一个变量,比如counter++,它必须从某个地方开始。

  8. 老实说,它看起来像是一些狡猾的程序代码,它已随意“转换”为C#。您需要完全重写此方法。您可能需要重做很多设计。


    这是一个重写类的示例,如果我已正确理解您的代码,将完成相同的任务(获取单个管理组和其子树中所有管理组的设备数量):< / p>

    public class DeviceRepository
    {
        private DeviceManagerEntities context;
    
        public DeviceRepository(DeviceManagerEntities context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            this.context = context;
        }
    
        public int GetDeviceCount(Guid managementGroupID)
        {
            return GetDeviceCount(new Guid[] { managementGroupID });
        }
    
        public int GetDeviceCount(IEnumerable<Guid> managementGroupIDs)
        {
            int deviceCount = context.Devices
                .Where(d => managementGroupIDs.Contains(
                    d.ManagementGroups.ManagementGroupID))
                .Count();
            var childGroupIDs = context.ManagementGroups
                .Where(g => managementGroupIDs.Contains(g.ParentId))
                .Select(g => g.ManagementGroupID);
            deviceCount += GetDeviceCount(childGroupIDs);
            return deviceCount;
        }
    }
    

    请注意,这仍然不会很好地执行,因为它使用每个子组的新查询来锤击数据库;为了实现这个目的,您需要在数据库本身中实现递归查询。

答案 1 :(得分:4)

您似乎误解了递归函数的工作原理:您没有返回结果(即使在函数末尾还有return!)。您似乎认为递归调用之间共享counter - 但情况恰恰相反。事实上,递归的原理是基于 no 共享发生的事实。

每次递归调用都会获得 new counter变量。将所有这些结果添加到一起是您的职责。例如,只需要递归锚点:

if (firstTime)
{
    firstTime = false;
    counter = 0;
    GetNumberOfDevicesForManagementGroup(managementGroupId, firstTime);
}

这是错的;它应该看起来像这样:

if (firstTime)
{
    return GetNumberOfDevicesForManagementGroup(managementGroupId, false);
}

这里重要的是返回结果。但是设置firstTime是不必要的(并且不常见),并且根本不需要设置counter

方法体的其余部分必须相应更改。

(另外,这个递归锚似乎毫无意义。它也可以省略。)

答案 2 :(得分:0)

counter初始化为零以使编译器静音!事实上 为什么不在函数之外使它静止?

static int counter = 0;
public static int GetNumberOfDevicesForManagementGroup(Guid managementGroupId, bool firstTime)
{
 ....
}

编辑:看来OP希望在函数范围内有一个变量,另一种方法是修改函数的签名以包含变量的参数{ {1}}并将其设为counter参数而不是......

希望这有帮助, 最好的祝福, 汤姆。

答案 3 :(得分:0)

这样写:

您可以将计数器值作为参数插入。

public static int GetNumberOfDevicesForManagementGroup(Guid managementGroupId, bool firstTime, int counterValue)
  {
     int counter = 0;
     counter = counterValue;

     using (var ctx = new DeviceManagerEntities())
     {
        if (firstTime)
        {
           firstTime = false;
           counter = 0;
           GetNumberOfDevicesForManagementGroup(managementGroupId, firstTime);
        }
        else
        {
           var groups = ctx.ManagementGroups
              .Where(x => x.ParentId == managementGroupId)
              .ToList();
           if (groups.Count != 0)
           {
              foreach (ManagementGroups group in groups)
              {
                 var devices = ctx.Devices
                    .Where(x => x.ManagementGroups.ManagementGroupId == group.ManagementGroupId)
                    .ToList();
                 foreach (Devices device in devices)
                 {
                    counter++;
                 }
                 GetNumberOfDevicesForManagementGroup(group.ManagementGroupId, firstTime);
              }
           }
           else
           {
              var devices = ctx.Devices
                    .Where(x => x.ManagementGroups.ManagementGroupId == managementGroupId)
                    .ToList();
              foreach (Devices device in devices)
              {
                 counter++;
              }
           }
        }
     }
     return counter;
  }

您可以将计数器值作为参数插入。

答案 4 :(得分:0)

正如其他人所建议的那样,如果您在声明时将counter初始化为零,则可以解决编译问题。但只有那一个。

但是......为什么使用firstTime参数作为累加器?为什么不完全删除它并且根本不使用累加器呢?

public static int GetNumberOfDevicesForManagementGroup(Guid managementGroupId)
{
     // *** Initialization
     int counter = 0;
     using (/* ... */)
     {
       // *** No first time special case
       var groups = // ...
       if (groups.Count != 0)
       {
          foreach (ManagementGroups group in groups)
          {
             // *** No need to call ToList() to count
             counter += ctx.Devices
                .Count(x => x.ManagementGroups.ManagementGroupId == group.ManagementGroupId)
             // *** Add recursive result
             counter += GetNumberOfDevicesForManagementGroup(group.ManagementGroupId);
          }
       }
       else
       {
          // *** Use LINQ to count
          counter = devices.Count(x => x.ManagementGroups.ManagementGroupId == group.ManagementGroupId);
       }
     }
     return counter;
  }