【问题标题】:Doing a fuse with a boolean用布尔值做保险丝
【发布时间】:2012-04-11 20:58:54
【问题描述】:

我有很多代码片段必须在初始化期间运行一次

我必须以这种方式使用布尔标志,因为它在事件中

bool _fuse;

void PerformLayout()
{
    Size size;

    if (!_fuse)
    {
        size = _InitialContainerSize;
        _fuse = true;
    }
    else
        size = parent.Size;

    // ...
}

因为它经常发生,所以我做了一些事情让这个布尔变量看起来像一个保险丝

所以我这样做了:

bool _fuse;

void PerformLayout()
{
    Size size;

    if (!Burnt(ref _fuse))
        size = _InitialContainerSize;
    else
        size = parent.Size;

    // ...
}

如果初始化为false,则查询结果一次返回false,切换为true,后续调用返回true。

public static bool Burnt(ref bool value)
{
    if (!value)
    {
        value = true;
        return false;
    }
    else
        return true;
}

当然,它有效,但我只是适度满意,我相信还有更优雅的解决方案。什么是你的?

【问题讨论】:

  • return value || !(value = true);(开个玩笑!)
  • 有趣。到目前为止,结果是三票赞成,三票结束。
  • 不是一个真正的问题。应该在代码审查网站上。不在这里。
  • 最好的解决方案是您的第一次实施。第二个不必要地隐藏了你的意图。将该变量重命名为 _oneTimeSizeInitializeDone 并忘记
  • 顺便说一句,我完全理解您希望避免这种重复。我认为,如果您只是将方法和变量的名称更改为更易于理解的名称,这可能是一个不错的模式(甚至可以使它成为一个非常小的类)。

标签: c# boolean code-design


【解决方案1】:

我认为这里避免重复的总体思路是正确的(即使重复非常小……但仍然如此)。只需将其封装并正确命名即可:

struct InitializerGuard {
    private bool hasRun;

    public bool HasRun() {
        if (hasRun)
            return true;
        hasRun = true;
        return false;
    }
}

用法:

InitializerGuard sizeInitializer;

void PerformLayout()
{
    Size size;

    if (!sizeInitializer.HasRun())
        size = _InitialContainerSize;
    else
        size = parent.Size;

    // ...
}

但是,如果您发现自己经常使用这种模式,这可能表明重构是有序的。也许只是为某些变量分配默认值?无论如何,为什么它们没有被初始化?

【讨论】:

  • 或者只是:Size size = HasRun() ? parent.Size : _initSize;
  • 这是优雅而清晰的。我喜欢。我想我要复制这个。 (也许我会将 HasRun 方法更改为属性?) - 我可以更好地重构我的代码的某些部分,但在许多情况下我别无选择,因为它在某些事件中使用,并且代码周围需要一些 init第一次开火。
  • @Laurent 属性不能有副作用。至少,这会搞砸你的调试器(如果你调试你的代码并为这个变量添加一个监视,这个属性会在任意时间被评估,从而改变你的行为)。所以如果你把它作为一个属性,你就会以“有趣”的方式破坏你的代码。 ;-)
【解决方案2】:

有很多方法可以实现这一点。您可以创建一个复杂的状态机来执行您的逻辑(最快),但在许多情况下,这将是矫枉过正。或者,您可以像现在一样跟踪保存实例状态的布尔值。您还可以决定将这两种解决方案组合成一个简单的状态机,使用以下方法(速度适中):

public class TestClass
{
    private Action performLayoutAction;

    public TestClass()
    {
        // initial state
        performLayoutAction = InitializePeformLayout;
    }

    public void PerformLayout()
    {
        performLayoutAction();
    }

    private void InitializePeformLayout()
    {
        // whatever 

        performLayoutAction = ContiniousPerformLayout;
    }

    private void ContiniousPerformLayout()
    {
        // whatever 
    }
} 

【讨论】:

    【解决方案3】:

    您可以使用可空类型和空合并运算符来声明Size 属性:

    Size? _containerSize;
    
    Size ContainerSize {
      get {
        return (_containerSize ?? (_containerSize = _InitialContainerSize)).Value;
      }
    }
    

    然后你可以像这样使用它:

    void PerformLayout() { 
      var size = ContainerSize;
      // ...
    }
    

    如果您要延迟初始化的类型是引用类型,它会变得更加简单。

    另一种选择是使用Lazy<T> 类型。这可以用于上述代码可能中断的多线程场景:

    Lazy<Size> _containerSize = new Lazy<Size>(() => _InitialContainerSize);
    
    void PerformLayout() { 
      var size = _containerSize.Value;
      // ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-11
      • 2016-06-08
      • 2014-03-08
      • 2019-08-25
      • 2016-03-28
      • 1970-01-01
      • 2016-11-28
      • 1970-01-01
      相关资源
      最近更新 更多