6

Before I found the Lazy<T> type, I was using the following pattern for implementing global singletons:

class DataModel
{
    public static XmlSerializer Serializer
    {
        get { return SerializerFactory.instance; }
    }

    static class SerializerFactory
    {
        internal static readonly XmlSerializer instance =
            new XmlSerializer(typeof(DataModel));
    }    
}

This pattern provides the following advantages:

  1. Type initialization is lazy.
  2. Type initialization is thread-safe.
  3. Singleton access is simply a direct field access with no method calls.

Recently I've come across a lot of posts suggesting Lazy<T> for implementing similar singleton access patterns. Is there any benefit that Lazy<T> (or LazyInitializer) would bring to this implementation?

glopes
  • 4,038
  • 3
  • 26
  • 29
  • 1
    I would think that is only beneficial if constructing those instances is costly (time, IO, memory wise) and it's use is not immediately expected at startup. But as always: measure first, then decide based on evidence. – rene Sep 15 '17 at 09:34
  • @rene I think that's correct for the Lazy Pattern in general. I read the OP more like wanting to know if it is beneficial to use `Lazy` over an explicitly coded Holder Pattern to achieve a Lazy Singleton. – Fildor Sep 15 '17 at 09:38
  • @rene Agreed. The question was more general, assuming situations in which this analysis has already been performed. – glopes Sep 15 '17 at 09:38
  • Both approaches work, but the danger with your approach is that it is necessary to make the `instance` a readonly field rather than a property - if someone changes it to use a property, it may no longer work correctly in all cases. The implementation is based on knowledge about some subtle details of static type initialization, including the effect that the `beforefieldinit` flag has on a type. Using `Lazy` requires no such low-level knowledge. – Matthew Watson Sep 15 '17 at 09:40
  • 5
    See [Jon Skeet's article on implementing a singleton](http://csharpindepth.com/Articles/General/Singleton.aspx) for more information. – Matthew Watson Sep 15 '17 at 09:41
  • 1
    This is **not** a singleton! It is a lazily instantiated static readonly property (of the class DataModel). – Fildor Sep 15 '17 at 10:05
  • @Fildor what is your definition of a singleton, and how does this pattern differ from it? – glopes Sep 15 '17 at 10:09
  • Both default `Lazy` and the above singleton approach will cache the exception - consider using `LazyWithNoExceptionCaching` instead - https://stackoverflow.com/a/42567351/34092 – mjwills Sep 15 '17 at 10:29
  • 1
    Honestly, I would suggest you use **neither** of your options for a singleton in 90% of cases. I would instead use an IoC container (e.g. Autofac), register the class as a singleton with the container and inject it as per normal. _Thus making unit testing easier._ – mjwills Sep 15 '17 at 10:40
  • 1
    "what is your definition of a singleton" - [Singleton Pattern](https://en.wikipedia.org/wiki/Singleton_pattern) : "_... the singleton pattern is a software design pattern that restricts the instantiation of a class to one object_" - You *can* have more than one instance of `XmlSerializer` thus it is not a singleton. The instance returned from any DataModel object will be the same, that's all. – Fildor Sep 15 '17 at 11:03
  • @Fildor fair enough, I agree the `XmlSerializer` class is itself not a singleton. The specific example in this case is more of a shared type cache. However many of the concerns are shared with the singleton pattern. The example was meant more as a simple illustration of the more general problem. – glopes Sep 15 '17 at 11:14
  • "However many of the concerns are shared with the singleton pattern." I agree with that. – Fildor Sep 15 '17 at 11:15

2 Answers2

13

Readability

This is how your code would look like when implemented with Lazy<T>:

class DataModel
{
    private static readonly Lazy<XmlSerializer> lazySerializer = 
       new Lazy<XmlSerializer>(() => new XmlSerializer(typeof(DataModel)));

    public static XmlSerializer Serializer
    {
        get { return lazySerializer.Value; }
    }
}
  1. You need less boilerplate code (no inner class),
  2. the purpose of your code is immediately obvious to everyone reading it,
  3. it is much easier to verify that the code is correct and thread-safe (no knowledge of subtle type initialization details required, a look at Lazy<T>'s documentation suffices),
  4. people reading your code won't wonder why you reinvented the wheel and whether there might be some subtle difference to the built-in feature.
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • You and OP are missing `private` parameterless constructor. With expression syntax getter is 1 liner. – Sinatr Sep 15 '17 at 09:41
  • Maybe this is true for people who are familiar with `Lazy` but when I first saw code like this, its purpose was not immediately obvious. Specifically, you need to know the semantics of the `Lazy` class, whereas the pattern I suggested uses purely language constructs. – glopes Sep 15 '17 at 09:41
  • @glopes In the pattern you use, do you know why `instance` must be a readonly field and not a property? Do you think that requirement is clear? – Matthew Watson Sep 15 '17 at 09:42
  • @Sinatr: I know, but since expression syntax could be used in the OPs case as well, I thought that it was unfair to use it in my example. – Heinzi Sep 15 '17 at 09:43
  • @glopes: True, the name `Lazy` heavily implies that this might have something to do with lazy initialization. – Heinzi Sep 15 '17 at 09:44
  • @MatthewWatson yes, and I agree that requirement may not be immediately obvious. However, when using a language I find it very important to know exactly what to expect from each keyword/pattern I use, so this code can also be a good learning experience for readers. – glopes Sep 15 '17 at 09:46
  • Singletons are good to be lazy. You get control when to initialize them too, static initialization is meh... – Sinatr Sep 15 '17 at 09:47
  • @Sinatr `Lazy` assumes initialization on first access, which are exactly the same assumptions of the pattern I suggested. – glopes Sep 15 '17 at 09:48
  • 5
    @glopes Well I think we'll just have to agree to disagree about the concept that using patterns that assume arcane knowledge about the language is a good thing. In a learning context, maybe - but in production code, not so much... :) – Matthew Watson Sep 15 '17 at 09:48
  • @Sinatr the pattern I suggested also does not require `private` parameterless constructor because the factory class has been marked `static`. – glopes Sep 15 '17 at 09:52
  • @glopes, `DataModel` need `private` constructor, otherwise `new DataModel()` somewhere is possible, not really a singleton. – Sinatr Sep 15 '17 at 09:54
  • 1
    @Sinatr the serializer is the singleton, not `DataModel`. However, I was actually missing the `static` qualifier on the `Serializer` property, which I now fixed. – glopes Sep 15 '17 at 09:54
  • @Sinatr the `XmlSerializer` for `DataModel` is only constructed once, and can be reused everytime someone needs to serialize/deserialize an instance of `DataModel`. – glopes Sep 15 '17 at 09:58
  • @glopes Isn't that more of a lazily instantiated static member than a "singleton" then? – Fildor Sep 15 '17 at 10:01
  • @Fildor you can call it a lazily initialized singleton, but technically it is still just a singleton from the API consumer's point of view. – glopes Sep 15 '17 at 10:08
  • `whereas the pattern I suggested uses purely language constructs` Do you use LINQ @glopes ? If so, why? Since it requires understanding more than purely language constructs (like for loops)... You use it because it **better expresses intent**. _Also, `Lazy` is useful for things **other** than singletons. So, the average developer is going to have to understand it **anyway**._ – mjwills Sep 15 '17 at 10:34
  • @mjwills I never said I don't use anything other than purely language constructs. I only indicated that I prefer to learn the language constructs in detail first before learning other constructs built on top of the language. – glopes Sep 15 '17 at 11:02
  • That is a fair approach @glopes . Just be aware that that approach is optimising for you, and perhaps not for **every** developer. **I**, for example, find the Lazy solution much clearer and simpler to read since it expresses intent so clearly (although, as in my comment on your question I would use IoC rather than `Lazy`). – mjwills Sep 15 '17 at 11:04
4

Lazy<T> is much more expressive. It is immediately clear that the value utilizes deferred initialization.

For example I use it in situations when the intialization of some resource is too heavy to put in the constructor and I don't want the API-consumer to call some explicit Initialize routine.

mbnx
  • 912
  • 5
  • 11