Feedback

C# - Eine verzögerte Erstellung eines Objektes

Veröffentlicht von am 29.06.2015
(0 Bewertungen)
Diese Klasse ermöglicht es ein Objekt erst dann zu erstellen, wenn man das Objekt wirklich braucht.
Das bedeutet, dass sich das Verhalten des Codes den Laufzeitbedingungen anpasst.

Dies ist vor allem dann nützlich, wenn der Konstruktor des Objektes sehr rechenintensiv oder speicherintensiv ist.

Die Verwendung des Codes ist ganz einfach, es gibt zwei Möglichkeiten:

// Erstellt eine RLazyInit-Instanz für ein Objekt mit einem Konstruktor ohne Parameter
RLazyInit<Te> v = RLazyInit<Te>.Create<Te>();
// Erstellt eine RLazyInit-Instanz für ein Objekt mit einem Konstruktor mit Parametern.
RLazyInit<Te> v2 = new RLazyInit<Te>(() => { return new Te("Hallo Welt", 9, ...) });


Sobald man das Objekt mittels Value erfragt, wird es erst erstellt.

Dies kann in vielen Situationen sehr praktisch sein:

1. Einfaches erstellen einer statischen Variable mit Wert.

// Dieses Beispiel funktioniert ohne RLazyInit und ist sehr umfangreich...
private static MyObject _current = null;
public static MyObject Current {
get {
if(_current == null) {
_current = new MyObject();
}
return _current;
}
}


Hingegen kann man das auch in nur einer Zeile Code erledigen:

public static RLazyInit Current = RLazyInit.Create();


2. Das Objekt erst erstellen, wenn es auch notwendig ist.
Dies ermöglicht es, die Parameter an den Konstruktor bereits zu übergeben, ohne das Objekt an der Stelle direkt zu starten.
Man kann so z.B. das Objekt im Haupt-Thread erstellen und die Parameter von der UI auslesen und dann das Objekt mit dem Konstruktor
in einen zweiten Thread auslagern und erst dort bei Bedarf erstellen.

Update [30.06.2015]: Informationen auf Antwort von @Jan Welker hierher kopiert.
//
// Die Funktion, die das Objekt vom Type @T erstellen sollte.
//
public delegate T RLazyInitFunc<T>();

//
// Ermöglicht die verzögerte Initialisation eines Objektes.
// Dies kann sehr praktisch sein, wenn der Construktor() sehr langsam arbeitet,
// das Objekt aber jetzt erstellt werden muss ohne auf den ctor() zu warten.
// Das Objekt wird erst erstellt, wenn über @Value auf dieses Zugegriffen wird.
//
public sealed class RLazyInit<T> 
{
	private readonly T _nullValue;
	private T m_value;
	private RLazyInitFunc<T> m_func;

	public RLazyInit(RLazyInitFunc<T> f)
	{
		m_func = f;
		_nullValue = default(T);
		m_value = _nullValue;
	}

	public T Value {
		get {
			if (!HasValue) {
				m_value = m_func(); 
			}
			return m_value;
		}
	}

	// Gibt an, ob das Objekt bereits erstellt wurde.
	public bool HasValue {
		get {
			return !_Equal(m_value, _nullValue);
		}
	}

	// 
	// Erstellt eine standart RLazyInit-Instanz,
	// die erwartet, dass der Type einen ctor() ohne Paramter hat.
	// Dieser wird dann auch verwendet beim Erstellen.
	//
	public static RLazyInit<TV> Create<TV>() where TV : new() {
		return new RLazyInit<TV>(() => {
			return new TV();
		});
	}

	private static bool _Equal(T a, T b) {
		return Object.Equals(a, b);
	}
}

4 Kommentare zum Snippet

Jan Welker schrieb am 30.06.2015:
Hast du ein Beispiel, wozu man die Funktion brauchen kann?
Thomas Roskop schrieb am 30.06.2015:
Ja, es gibt einige Fälle, wo man soetwas nutzen kann (vor allem um sich Schreibarbeit zu sparen):

1. Unvorhersehbare Lebenszeit
Wenn du ein Objekt hast, wo du nicht weißt, OB oder WANN es aufgerufen wird und es aber von überall statisch aufrufbar sein muss,
kannst du den Standart-Code dazu verwenden:
 
private static MyObject _current = null;
public static MyObject Current {
get {
if(_current == null) {
_current = new MyObject();
}
return _current;
}
}
// Zugriff: Current.myFunction();

Oder der kurze Weg:

public static RLazyInit<MyObject> Current = RLazyInit<MyObject>.Create<MyObject>();
// Zugriff: Current.Value.myFunction();


2. Alle Paramter zum Erzeugen schon vorzeitig übergeben, aber erst anderswo (wenn überhaupt notwendig) erzeugen.
Dies macht vorallem dann Sinn, wenn der Konstruktor sehr umfangreich ist und lange Ladezeiten hat.

EDIT: Leider scheint die Seite im einen Fehler zu haben, es fehlen die spitzen Klammern mit dem MyObject in der Mitte?!
Jan Welker schrieb am 30.06.2015:
Danke! Mit der Erklärung kann man es nachvollziehen.
Koopakiller schrieb am 30.06.2015:
Ab .NET 4.0 würde ich eher die Build-In Lazy(T) Klasse nutzen.
 

Logge dich ein, um hier zu kommentieren!