12

I'm working on an object in C# where I need each instance of the object to have a unique id. My solution to this was simply to place a member variable I call idCount in the class and within the constructor I would have:

objectID = idCount;
idCount++;

I thought that this would solve my problem but it seems that idCount never gets incremented even though the constructor gets called multiple times. For example if idCount = 1, the objectID for all the objects are still 1. Why doesn't idCount++ work?

Any help would be appreciated. Apologies if my explanation isn't adequate, I'm not sure how else to explain it.

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
ivarlee
  • 161
  • 2
  • 2
  • 6
  • 1
    You want a static variable for idCount. – Daryl Teo Jan 11 '12 at 02:33
  • 1
    If you don't need strictly incrementing integer, a new GUID can make for a unique ID. You will also need to account for threaded access -- if applicable -- when using the "static" approach. –  Jan 11 '12 at 02:56

5 Answers5

29

You need a static property in your class, BUT, you need to assign it to an instance variable within the class if you want each object to contain the id it was created with.

Also, you'll want to use Interlocked.Increment on the counter in case you are updating multiple instances simultaneously:

    public class Foo
    {
        private static int m_Counter = 0;

        public int Id { get; set; }

        public Foo()
        {
            this.Id = System.Threading.Interlocked.Increment(ref m_Counter);
        }
    }
competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • I think it's simpler if we use `volatile` instead of `Interlocked.Increment`? – Cheng Chen Jan 11 '12 at 02:46
  • 2
    @DannyChen: It may be simpler, but it's not safer. See this discussion for more details: http://stackoverflow.com/questions/154551/volatile-vs-interlocked-vs-lock – competent_tech Jan 11 '12 at 02:51
  • 1
    @competent_tech: It seems that I misunderstood this keyword for a few years...oh GOD! – Cheng Chen Jan 11 '12 at 03:27
  • Does Interlocked.Increment really work here? Consider the following: 1. Thread A increments m_Counter; 2. Thread B increments m_Counter; 3. Thread A copies value of m_Counter to instanceA.Id; 4. Thread B copies value of m_Counter to instanceB.Id. Now both instanceA and instanceB have the same id. How can Interlocked.Increment prevent that? It only ensures that m_Counter is incremented atomically; it can't ensure that its value gets read in any particular order after the call to Interlocked.Increment returns. – Klitos Kyriacou Mar 11 '13 at 17:44
  • 1
    @Klitos Interlocked.Increment returns the incremented value so no race condition here, nothing is copying the m_Counter field directly to instanceA.Id or instanceB.Id – Neil Mosafi Apr 05 '13 at 11:03
  • @NeilMosafi I see what you mean. Thanks for pointing this out. – Klitos Kyriacou Apr 06 '13 at 20:17
2

You could use a static variable in your class that gets updated when the object is initialized.

public class Foo
{
   private static int ID = 0;
   private int myId = 0;

   public int MyId
   {
      get { return myId; }
   }

   public Foo()
   {
       ID++;
       this.myId = ID;
   }
}
Brownman98
  • 1,053
  • 1
  • 7
  • 17
  • +1 - the key is the increment value is static but the instance id is still unique. However, this is not thread safe - see suggestion in @sablinkenlights answer about interlocked if you want it to be thread safe. – bryanmac Jan 11 '12 at 02:38
1

As everyone has pointed out, static variables are the specific answer to your question. But static variables only have scope within the process in which they were created and there is no relationship across processes (for example, a load balanced web environment).

If what you are looking for is a unique way to identify an object instance for the duration of its lifetime, I suggest something like:

byte[] bytes = new byte[8];

RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();            
crypto .GetBytes( bytes );

long id = BitConverter.ToInt64( bytes, 0 );

This will give you a random number which has an extremely low (roughly 0-1 in 100,000,000) chance of collision and you don't need to worry about keeping track of it.

Tim M.
  • 53,671
  • 14
  • 120
  • 163
1

You set IdCount is static member of MyObject.

public class MyObject
    {
        static int idCount = 0;

        private int _objectID;
        public int ObjectID
        {
            get { return _objectID; }
        }


        public MyObject()
        {
            idCount++;
            _objectID = idCount;
        }
    }
0
public sealed class SingletonIdGenerator
{
    private static long _id;
    private SingletonIdGenerator()
    {
    }

    public string Id
    {
        get { return _id++.ToString().Substring(8); }
    }

    public static SingletonIdGenerator Instance { get { return Nested.instance; } }

    private class Nested
    {
        static Nested()
        {
            _id = DateTime.Now.Ticks;
        }
        internal static readonly SingletonIdGenerator instance = new SingletonIdGenerator();
    }
}
Md Abu Zafar
  • 142
  • 6