Je n'ai aucune idée quelles sont les différences entre la classe LazyInitializer et Lazy <T> . Je sais qu'ils initialiseront tous les deux l'objet uniquement sur demande. Quand dois-je utiliser chacun d'eux?
Réponses
Trop de publicités?Je ne sais pas si vous êtes toujours à la recherche de cette, mais j'ai dû plonger dans les détails des deux Lazy<T>
et LazyInitializer.EnsureInitialized<T>()
récemment, j'ai donc pensé que je devais partager mes découvertes.
Tout d'abord, quelques chiffres. J'ai couru des repères à l'aide de deux méthodes sur des lots de dix millions de valeurs à l'aide de ces deux approches, les tests pour l'utilisation de la mémoire avec GC.GetTotalMemory(true)
et prise en Stopwatch
horaires pour l'instanciation, la première valeur de l'accès, et à la suite de la valeur accède à:
Lazy<T> Memory Use: 320,000,000 bytes (32B/instance)
EnsureInitialized<T>() Memory Use: N/A
Lazy<T> Instantiation Time: 622.01 ms
EnsureInitialized<T>() Inst. Time: N/A
Lazy<T> First Access: 1,373.50 ms
EnsureInitialized<T>() First Access: 72.94 ms
Lazy<T> Subsequent Accesses: 18.51 ms
EnsureInitialized<T>() Subsequent: 13.75 ms
(J'ai utilisé LazyThreadSafetyMode.PublicationOnly
avec l' Lazy<T>'s
, ce qui semble être le même thread en matière de sécurité prises par LazyInitializer
par défaut).
Comme vous pouvez le voir, à moins que j'ai foiré mes tests en quelque sorte (jamais hors de question!), dans ces circonstances, LazyInitializer
est supérieure à la juste au sujet de chaque moyen quantifiable. Il n'a pas de mémoire ou de l'instanciation de la surcharge, et c'est plus rapide à la fois pour la création et la récupération de la valeur.
Alors, pourquoi voudriez-vous d'utiliser Lazy<T>
? Eh bien, tout d'abord, ce furent les résultats de test sur mon système x64, et il est possible que vous pourriez obtenir des résultats différents dans d'autres circonstances.
Lazy<T>
peut également entraîner plus clair et plus concis code. return myLazy.Value;
est beaucoup plus convivial que return LazyInitializer.EnsureInitialized(ref myValue, () => GetValue(foo));
En outre, Lazy<T>
rend les choses beaucoup plus simple si vous avez affaire à un type de valeur, ou avec un type de référence qui pourrait être légitimement null
. Avec LazyInitializer
, vous devez utiliser un deuxième champ booléen pour savoir si la valeur a été initialisé, la préparation du code de la clarté question. Lazy<T>
est aussi plus simple à utiliser si vous souhaitez plus strictes de sécurité des threads.
Et dans le grand schéma des choses, la plupart des frais généraux est probablement négligeable pour un grand nombre d'applications (bien que pas toujours -- la raison que j'ai commencé à la recherche dans ce est parce que je travaillais sur une demande portant sur des millions de très petites paresseusement-chargé de valeurs, et les 32 octets par exemple la surcharge de l' Lazy<T>
était en train de commencer à devenir gênant).
En fin de compte, sauf si votre application est très gourmand en mémoire, je pense qu'il est généralement va être une question de préférence personnelle. Pour les non-null types de référence, personnellement, je pense que LazyInitializer.EnsureInitialized<T>()
est un plus élégant, mais je peux creuser le code de la clarté de l'argument trop.
Lazy<T>
(MSDN) est un wrapper générique qui permet de créer une instance de T
, sur demande, par la tenue d'un T
méthode de fabrique (Func<T>
) et de l'appeler quand Vaue
getter de la propriété est accessible.
LazyInitializer
- statique de la classe à un ensemble de méthodes statiques, c'est juste un helper qui utilise de l'Activateur.CreateInstance() (réflexion) pouvoir instancier un type donné d'instance. Il ne garde pas tout local privé de champs et de ne pas exposer les propriétés, donc pas de l'utilisation de la mémoire de frais généraux.
Intéressant de noter que les deux classes d'usages Func<T>
comme exemple de l'usine.
MSDN dit en quelques mots à propos de LazyInitializer
classe:
Ces routines d'éviter d'avoir besoin d'allouer un dédié, paresseux-initialisation de l'instance, au lieu d'utiliser des références à assurer des objectifs ont été initialisé comme ils sont accessibles.
PS:
Je l'ai trouvé intéressant un moyen LazyIntiializer
vérifie si l'instance déjà initialisée, il juste de comparer un passé en référence à un default(T)
, nice:
private static T EnsureInitializedCore<T>(ref T target, Func<T> valueFactory)
where T : class
{
T t = valueFactory();
if (t == null)
{
throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation"));
}
Interlocked.CompareExchange<T>(ref target, t, default(T));
return target;
}
Ce qui me semble étrange, il crée une nouvelle instance à chaque fois avant un chèque:
T t = valueFactory();
// ... and only then does check
LazyInitializer
vous permet la fonctionnalité d'initialisation paresseuse sans la surcharge de création d'une classe pour chaque objet initialisé paresseusement.
Voici les avantages que LazyInitializer
peuvent offrir.
Il appartiendra à vos propres exigences de savoir si la surcharge créée en utilisant Lazy<T>
est trop pour la situation.
La documentation sur l'Initialisation différée, il explique assez clairement. Voir L'Initialisation Tardive. En bref, Lazy<T>
crée une nouvelle classe (la construction d'un générique) pour chaque T
que vous utilisez, et une nouvelle instance de cette classe pour chaque T
exemple vous decalre -- même si le sous-jacent T
n'est jamais initialisée. À l'aide de la statique des méthodes d' LazyIntializer
peut être plus complexe pour le codage, mais évite les frais généraux de l' Lazy<T>
wrapper instances.
Je pense que cela répond à votre question: Une autre façon de LazyInitialization Système.Le filetage.ThreadLocal
C'est le même que Paresseux, mais la seule différence est qu'il stocke les données sur le Thread Local de base. Les valeurs sur chaque Thread serait une autre copie de l'objet Initialisé.
plus de détails à partir de: http://www.cshandler.com/2011/09/different-ways-of-lazy-initialization.html