Thursday, January 30, 2014

Singleton uniqueness issue in a C# generic class

I code in C# within the Unity environment (I really like the co-routine mechanism and the yield return instruction).

At one point, I developed a set of base classes to provide a default behavior to entities that must be exchanged on the wire and cached on the device. And I used generic/template classes to be able to generate instances of the final classes from within the methods of the super class. Information saved on the device are signed with a piece of information obtained from the server.

public class LocalCache<T> where T : BaseModel {

    public static string signatureKey { get; set; }

    public T Get(long id) {
        // Get the JSON from PlayerPrefs
        // Verify its signature
        return (T) Activator.CreateInstance(typeof(T), new object[] { JSON });
    }

    public void Save(T entity) {
        // Get the JSON representing the data
        // Sign the JSON
        // Save the signed JSON into PlayerPrefs
    }

    public void Reset(long id) { ... }

    private string SignData(string data) { ... }
}

In Java, there would be only one instance of the signatureKey variable per JVM, whatever the number of class instances. In C#, I've been surprised to find that this attribute is not unique!

public class LocalCache<T> where T : BaseModel {

    // Other code as shown above...

    public static void TestDifferentInstanciations() {
        LocalCache<System.Int64>.signatureKey = "Set for Int64";

        UnityEngine.Debug.Log("Int16: " + LocalCache<System.Int16>.signatureKey);
        // => produces: Int16:

        UnityEngine.Debug.Log("Int16: " + LocalCache<System.Int64>.signatureKey);
        // => produces: Int64: Set for Int64

        UnityEngine.Debug.Log("string: " + LocalCache<System.string>.signatureKey);
        // => produces: string:
    }
}

My surprise comes from the fact that the concept of generic class only exist for the definitions. At runtime in C# only exist types and the type of LocalCache<Int16> is different from the type of LocalCache<Int64>. Then there are multiple copies of the signatureKey attribute, exactly one per constructed types...

The solution is to set the singleton into a separated non generic class!

internal static class StaticLocalCacheInfo {
    public static string signatureKey { get; set; }
}

public class LocalCache<T> where T : BaseModel {

    public static string signaturePartFromServer {
        get { return StaticLocalCacheInfo.signatureKey; }
        set { StaticLocalCacheInfo.signatureKey = value; }
    }

    // Rest of the business logic...

    public static void TestDifferentInstanciations() {
        LocalCache<System.Int64>.signatureKey = "Set for Int64";

        UnityEngine.Debug.Log("Int16: " + LocalCache<System.Int16>.signatureKey);
        // => produces: Int16: Set for Int64

        UnityEngine.Debug.Log("Int16: " + LocalCache<System.Int64>.signatureKey);
        // => produces: Int64: Set for Int64

        UnityEngine.Debug.Log("string: " + LocalCache<System.string>.signatureKey);
        // => produces: string: Set for Int64
    }
}

Note that I got the confirmation of the behavior on Unity forum.

I hope it helps!
A+, Dom

No comments:

Post a Comment