【问题标题】:How to run a method in a loop only once?如何在循环中只运行一次方法?
【发布时间】:2012-10-19 23:49:42
【问题描述】:

我正在使用一个开关作为我的 XNA 游戏的状态管理器。该开关是主要更新方法的一部分,因此它每帧都运行。有时我需要设置一个计时器值,并且每个方法调用只能设置一次。有多种方法可以根据情况设置计时器,因此它不能使用当前和以前的状态编号来检查是否可以覆盖以前的时间。

case "state 34": {
SetTime(theTime); // should run only once
// other things
if (TheTimeisRight(time)) // runs every call
    {
        SetTime(theTime); // should run only once
        if (TheTimeisRight(time)) 
        { /*  some methods  */ }
    }
break; }

我怎样才能做到这一点,或者有没有更好的方法来做到这一点而无需离开交换机? (改变 SetTime 方法是可以的,但我不想用额外的代码把开关弄乱)

【问题讨论】:

  • 能否添加完整的 Switch 代码,并详细解释一下
  • @Anandkumar 有什么不清楚的开关?我在问题中给出了我的开关示例,每种情况都与您在那里看到的大致相同,只是检查,方法和参数不同。
  • 是每个方法调用,每个帧吗? IE 每帧一次?
  • @Alan 开关每帧调用一次,而 SetTime 在代码中每次调用只应调用一次,因此如果连续有两个 SetTime 方法,则应分别调用一次。
  • 好的,我想我明白了。你反对修改switch语句的签名吗?

标签: c#


【解决方案1】:

另一种方法:在要调用的方法周围引入一个包装器:

    public sealed class RunOnceAction
    {
        private readonly Action F;
        private bool hasRun;

        public RunOnceAction(Action f)
        {
            F = f;
        }

        public void run()
        {
            if (hasRun) return;
            F();
            hasRun = true;
        }
    }

然后在 switch 语句之前创建var setTimeOnce = new RunOnceAction(() => SetTime(theTime));,并在那里调用setTimeOnce.run()。根据需要调整参数/返回值。

【讨论】:

    【解决方案2】:

    如果您不想弄乱布尔变量ala hasSetTimeAlready,您可以随时引入另一个调用该方法的状态,然后进入原始状态。

    【讨论】:

    • 我宁愿不使用其他状态,一个地球半径长开关已经足够了。
    • 如果你的开关已经太长了,你应该为你的状态使用类。导致小类爆炸式增长,但保持代码可读性。
    • 在最简单的情况下,只需定义一个接口State,例如,一个 Name 属性和一个执行任何操作并返回要切换到的下一个状态的 Execute 方法。对于更复杂的问题,dotnet.zcu.cz/NET_2006/Papers_2006/short/B31-full.pdf 有一些不错的想法。
    【解决方案3】:

    将调用置于循环之外。
    您可能需要一个单独的条件语句来确定它是否应该运行,但这比尝试使用标志和/或各种其他臭代码方法来控制调用的重复要好得多。

    编辑:

    我的意思是把它放在开关之外的一个地方

    if (someCondition && someOtherCondition && yetAnotherCondition)  
        setTime(theTime); // just one call, in one place, gets executed once
    
    switch(someValue) 
    {
        case "state 34": {
            //SetTime(theTime); // no longer necessary
            // other things
            if (TheTimeisRight(time)) // runs every call
            {
                //SetTime(theTime); // no longer necessary
                if (TheTimeisRight(time)) 
                { /*  some methods  */ }
            }
            break;
    
        ...etc...
    }
    

    忠告:对你的开关值使用枚举而不是字符串。

    说实话,这几乎是任何人都可以在没有看到更完整的代码示例的情况下实际帮助您的程度(我认为您提供给我们的示例有些做作,并且与您所拥有的不太准确?)。解决此问题的最佳方法可能是解构 switch 语句并重新开始,因为维护状态机不是处理这种情况的最佳方法,或者您需要引入一些其他状态。

    【讨论】:

    • 我想把所有东西都放在一个地方,但我想这不是一个简单的方法。
    • @user1306322 但这并不是真正的“都在同一个地方”,因为你有多个地方可以调用它。把它放在switch 之外肯定会把它放在一个地方。
    • 我不明白 putting outsidekeeping in one place 是怎么回事……好吧,这对我来说没有意义 :)
    • @user1306322 你最终会一个调用函数如果满足正确的条件,你不会有几个潜在的调用乱七八糟编码。将它放在switch 之外可能是最简单的解决方法 - 保留它可能需要重新编写你的 switch 语句,除非你愿意使用容易出错的臭代码。
    • @user1306322 如果您在条件语句中使用快捷方式,则执行单独的条件语句根本不会减慢代码速度。
    【解决方案4】:

    我已经使用HashSet<int> 来检查当前的SetTime(time, num) 方法之前是否没有使用if (!hashSet.Contains(num)) 调用过。

    void SetTime(int time, int num)
    {
        if (!hashSet.Contains(num))
            {
                theTime = time;
                hashSet.Add(num);
            }
    }
    

    确实看起来不太酷,但可以工作并且不会过多(视觉上)损坏方法调用,因此保存了开关的可读性。

    【讨论】:

      猜你喜欢
      • 2021-04-27
      • 1970-01-01
      • 2018-05-02
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      • 2011-11-02
      • 2013-11-28
      • 1970-01-01
      相关资源
      最近更新 更多