简体   繁体   中英

Why won't this static variable increment when using generics?

I need a certain class to contain a static member that keeps track of everytime an instance of that class is instantiated, essentially so that each instance of the class has a unique index. It works with a non-generic class but this generic implementation fails whenever the type T differs between instances:

class A<T>
{
   private static int counter;

   private static int Counter {
       get { 
          Increment(); 
          return counter; 
       }
   }

   private static void Increment() {
       counter++; 
   }

   public int Index; 

   public A()
   {
       this.Index = Counter; // using A<T>.Counter makes no difference

       Console.WriteLine(this.Index);      
   }
}


class Program
{
    static void Main(string[] args)
    {
        var a = new A<string>();
        var b = new A<string>(); 
        var c = new A<string>();
        var d = new A<int>(); 
    }
}

The output is:

1

2

3

1

As soon as the type T switches to int instead of string, the counter resets.

Does this fail by design, and if so what is the reason or how can I get around it? Or is it a bug? It makes sense to some degree because the type T, being generic, is in the class declaration, but..

Each different T creates a new class for A<T> and hence distinct static counters.

To get around this you can use inheritance like so:

abstract class A
{
   protected static int counter;
}

class A<T> : A
{
   private static int Counter {
       get { 
          Increment(); 
          return counter; 
       }
   }

   private static void Increment() {
       counter++; 
   }

   public int Index; 

   public A()
   {
       this.Index = Counter;

       Console.WriteLine(this.Index);      
   }
}

Not a bug - this is by design, and is a consequence of how generics work.

A generic type like your A<T> serves as a template - when you use type parameters, the compiler generates an actual class with that type T , and a different one will be created for each different type T .

This explains the results you see - there is a static field for the A<int> and another one for the A<string> .

This is because different types are generated under the hood for classes with different generic type parameters. This difference is only for the value type parameters as kindly noted by Ben in comment.

Check out these MSDN articles:

EDIT:

Consider following code:

public abstract class GenericBase<T>
{
    public static int Counter { get; set; }        
}

public class GenericInt : GenericBase<int>
{        
}

public class GenericLong : GenericBase<long>
{        
}

public class GenericDecimal : GenericBase<decimal>
{        
}

[TestFixture]
public class GenericsTests
{
    [Test]
    public void StaticContextValueTypeTest()
    {
        GenericDecimal.Counter = 10;
        GenericInt.Counter = 1;
        GenericLong.Counter = 100;

       // !! At this point value of the Counter property
       // in all three types will be different - so does not shared across
       // all types
    }
}

A generic class is a template from which other classes are created. A List<String> and a List<int> are two completely different classes, despite them both originating from List<T> .

Have your generic classes reference a non-generic class that holds the counter. Do not put the static class inside the generic class. This will cause the static class to be generated for each value of T .

class A<T>
{
    private static int Counter {
        get {
            ACounter.Increment();
            return ACounter.counter;
        }
    }

    public int Index;

    public A()
    {
       this.Index = Counter;

       Console.WriteLine(this.Index);
    }
}

static class ACounter
{
    static ACounter() {
        counter = 0;
    }

    public static int counter {get; private set;};

    public static void Increment() {
        counter++;
    }
}

Generics with different type parameters are different types. So A<int> and A<string> are different classes, and so are allocated different statics.

This is by design. An instance of A<int> is not an instance of A<string> they are different classes so there are 2 static variable one for each class.

A<int> is actually a different class than A<string> , hence they have different static counters

This is why Resharper flags static variables in generics because so few programmers seems to understand statics and especially statics in generics

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM