tags:

views:

169

answers:

3

I ran across the following class in a C# XNA graphics api and I am not sure what it does or that it needs to be so obscure. (T is constrained to be a struct in a parent class)

    static class Ident
    {
        static object sync = new object();
        static volatile int index = 0;
        static int Index
        {
            get
            {
                lock (sync)
                    return index++;
            }
        }
        class Type<T>
        {
            public static int id = Index;
        }
        public static int TypeIndex<T>()
        {
            return Type<T>.id;
        }
    }

The API makes only on call to this static class: int index = Ident.TypeIndex<T>();

+2  A: 

It returns the number of times Ident.TypeIndex was called, probably to assign a unique number to each object.

Because of the way they are using generics, there should be a different sequence of numbers for every type T. So you could have a #1 circle, a #2 circle, and a #1 square.

Jonathan Allen
Nice ... the code could use some comments for sure.
Hamish Grubijan
Actually - that's not true. You get a unique int for each Type, because each Type<T> uses a single static id. You don't get a new sequence per type.
Reed Copsey
If you call this, you'd get 0: Circle, 0: Circle, 1: Square, 0: Circle, etc...
Reed Copsey
+8  A: 

It assigns an application-wide (static) and thread-safe(locking sync object + volatile index) unique identifier for each different type T.

example :

Console.WriteLine(Ident.TypeIndex<int>()); // 0
Console.WriteLine(Ident.TypeIndex<string>()); // 1
Console.WriteLine(Ident.TypeIndex<long>()); // 2
Console.WriteLine(Ident.TypeIndex<int>()); // 0

Volatile is being used to ensure that the current thread doesn't cache the index value and locking prevents more than one thread to access it.

Diadistis
+1 for the mention of the threading implications.
Adam Robinson
+7  A: 

This creates a unique integer identifier for each Type, based on the order that it's accessed, in a thread-safe manner.

For example, if you do:

int myClassId = Ident.TypeIndex<MyClass>();
int mySecondClssId = Ident.TypeIndex<MySecondClass>();

You'll get 2 "TypeIndex" numbers (with mySecondClassId being at least 1 more than myClassId, but potentially greater, due to threading). Later, if you call this again with the same class, it will return the same TypeIndex for that class.

For example, if I run this, using:

Console.WriteLine(Ident.TypeIndex<Program>());
Console.WriteLine(Ident.TypeIndex<Test>());
Console.WriteLine(Ident.TypeIndex<Program>());
Console.WriteLine(Ident.TypeIndex<Test>());

It will print:

0
1
0
1

However, this could be done more effectively using Interlocked.Increment, which would avoid the need for the lock and the synchronization object completely. The following gives exactly the same answer, with no locking required:

static class Ident
{
    static int index = -1;
    static int Index
    {
        get
        {

            return Interlocked.Increment(ref index);
        }
    }
    private static class Type<T>
    {
        public static int id = Index;
    }

    public static int TypeIndex<T>()
    {
        return Type<T>.id;
    }
} 
Reed Copsey
+1 for better alternative
Diadistis
Curious - why the downvoting?
Reed Copsey
Speculation - Ignorance
Diadistis
I'm just always curious - I don't mind downvotes - it's the best way to learn something new ;) I just like knowing why somebody disagrees...
Reed Copsey
I'm not the downvoter - but it could be that they see someone with such a high rep get the accepted answer instead of Diadistis who answered the OP's question a full 6 minutes sooner.
Josh Einstein
Could be, but he got downvoted before the answer was accepted.
Diadistis
Yeah - its okay - Its not a big deal - I was just curious - I always like to know why people are downvoting, when possible, because that's the best opportunity to learn. However, my answer and Diadistis's were somewhat different, especially early on...
Reed Copsey