Делать предохранитель с логическим значением - PullRequest
5 голосов
/ 28 марта 2012

У меня много фрагментов кода, которые необходимо выполнить один раз во время инициализации.

Я должен использовать логический флаг таким образом, потому что он находится в событии

bool _fuse;

void PerformLayout()
{
    Size size;

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

    // ...
}

Поскольку это случается часто, я сделал что-то, чтобы эта логическая переменная выглядела как fuse :

Итак, я сделал это:

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;
}

Конечно, это работает, но я только умереннодоволен и уверен, что есть более элегантные решения.Что будет твоим?

Ответы [ 3 ]

1 голос
/ 28 марта 2012

Есть много способов достичь этого. Вы можете создать сложный конечный автомат, выполняющий вашу логику (самый быстрый), но во многих случаях это будет излишним. В качестве альтернативы, вы можете отслеживать логическое значение, которое хранит состояние вашего экземпляра, как у вас сейчас. Вы также можете объединить оба решения в простой конечный автомат с помощью методов (умеренно быстрых):

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 
    }
} 
1 голос
/ 28 марта 2012

Я думаю, что общий смысл в том, чтобы избежать повторения, здесь правильный (даже если повторение очень мало ... но все же). Просто инкапсулируйте его и назовите правильно:

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;

    // ...
}

Но если вы обнаружите, что используете этот шаблон очень часто, это может указывать на то, что рефакторинг в порядке. Может быть, просто назначить значения по умолчанию для некоторых переменных? В любом случае, почему они не инициализированы?

1 голос
/ 28 марта 2012

Вы можете использовать обнуляемые типы и оператор объединения нулей для объявления свойства 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;
  // ...
}
...