Lazy Loading kontekstu aplikacji

W większościach aplikacji, kontekst ładujemy na starcie. Wtedy mamy najwięcej czasu na zużycie jak największych zasobów procesora przez większą ilość czasu. System ten sprawdza się w grach, zdarzają się jednak aplikacje, które nie zawsze korzystają z załadowanego kontekstu. Warto wtedy zastosować, coś co nazywa się “leniwym ładowaniem”. Oznacza to, że odpowiednie zasoby ładujemy dopiero w chwili kiedy się do nich odwołujemy po raz pierwszy. Dajmy na to, że mamy obiekt ApplicationContext, który zawiera wiele elementów, które aplikacja przy zamykaniu zapisuje w pamięci, natomiast przy uruchamiania wczytuje je z pamięci. Obiekt ten zawiera również jakiś obiekt BigObject, do którego to elementu odwołujemy się tylko w specjalnych sytuacjach, dlatego nie chcemy tego obiektu ładować przy starcie.

public class ApplicationContext
{
    public BigObject BigObject { get; private set; }

    public void LoadContext() {
        BigObject = LoadObject();
    }

    private BigObject LoadObject() {
        //loading object from memory
    }
}

Wystarczy mała zmiana kodu aby stworzyć obiekt, który ładuje się dopiero po odwołaniu się do niego. Ciekawe jest to, jak niewiele kodu trzeba napisać, żeby osiągnąć pożądany efekt.

public class ApplicationContext
{
    private BigObject _bigObject;
    public BigObject BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private BigObject LoadObject() {

    }
}

Często jednak wczytanie kontekstu wiąże się z odczytywaniem czegoś z pamięci, więc z operacjami, które mogą trwać sporo czasu jeśli obiekt do załadowania jest sporych rozmiarów. Dzięki temu, że w C# można tak łatwo tworzyć funkcje asynchroniczne, po małej modyfikacji (wystarczy opakować zmienne w klasę Task) możemy stworzyć obiekt, który wczytywać się będzie asynchronicznie.

public class ApplicationContext
{
    private Task<BigObject> _bigObject;
    public Task<BigObject> BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private Task<BigObject> LoadObject() {
        //loading object from memory
    }
}

W tym przypadku należy pamiętać, aby przed odwołaniem się do właściwości BigObject postawić słówko kluczowe await.

var bo = await appContext.BigObject;

Jednak czekać na wczytanie danych będziemy tylko za pierwszym razem, każde kolejne odwołanie się do BigObject zwróci nam jego wartość automatycznie.