views:

99

answers:

3

Hi,

I have this code:

public class EntityMapper<T> where T : IMappingStrategy, new()
{
    private static T currentStrategy;

    public static T CurrentStrategy  
    {
        get
        {
            if (currentStrategy == null)
                currentStrategy = new T();

            return currentStrategy;
        }
    }


}

Then:

    public static void Main()
    {
        EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
        EntityMapper<ClientMappingStrategy>.CurrentStrategy.ToString();
        EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
    }

Well, the question is:

Why when i'm debugging i can see that the constructor of ServerBussinessMappingStrategy is called only once time?

This work well, but i undertand why always EntityMapper return the correct instance that i need, only instantiating once time the ServerMappingStrategy class.

Regards!

PD: Sorry my english jeje ;)

+5  A: 

The static field is persisted for the duration of your AppDomain, and it is cached when first created:

public static T CurrentStrategy  
{
    get
    {
        if (currentStrategy == null) // <====== first use detected
            currentStrategy = new T(); // <==== so create new and cache it

        return currentStrategy; // <=========== return cached value
    }
}

Actually, there is an edge case when it could run twice (or more), but it is unlikely.

This is a pretty common pattern for deferred initialization, and is used pretty much identically in a number of places in the BCL. Note that if it had to happen at most once, it would need either synchronization (lock etc) or something like a nested class with a static initializer.

Marc Gravell
The synchronization issue is why the best practice is to initialize it in the declaration. Jon Skeet goes into this in much more detail here: http://www.yoda.arachsys.com/csharp/singleton.html
Steven Sudit
+1  A: 

Normally, it will only get called once. That is, unless you have a race condition.

Let's say two threads execute this statement the same time:

EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();

Let's say thread A will run up until currentStrategy == null but gets paused before new T() when Windows suddenly gives control to thread B which then makes the comparison again, currentStrategy is still null, invokes the constructor and assigns the new instance to currentStrategy. Then, at some point, Windows gives the control back to thread A that calls the constructor again. This is important because normally static members are (sort of) expected to be thread safe. So if I were you, I would wrap that bit into a lock clause.

P.S. this snippet won't compile as T might be a struct that cannot be a null. Instead of comparing to null, compare to default(T) or specify that T has to be a class.

DrJokepu
A: 

OK, good, but how the runtimemanager known what is the memory location for the diferent types of currentStrategy? Because if you test this code:

EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ClientMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ClientMappingStrategy>.CurrentStrategy.ToString();
            EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();

You can see that the constructors of ClientMappingStrategy and ServerMappingStrategy are called once, and the memory location (obtained by & operator, for private static T currentStrategy) is the same for each type, for example, for all ServerMappingStrategy currentStrategy is 5x1234 and for all ClientMappingStrategy currentStrategy is 5x5678. Then the cuestion is:

how the runtime manager remember the two diferent currentStrategy adresses according each type?

Again, sorry my english.

killeroverflow