Почему эта статическая переменная не увеличивается при использовании шаблонов? - PullRequest
28 голосов
/ 16 ноября 2011

Мне нужно, чтобы определенный класс содержал статический член, который отслеживает каждый раз, когда создается экземпляр этого класса, по существу, так, чтобы каждый экземпляр класса имел уникальный индекс.Он работает с неуниверсальным классом, но эта универсальная реализация завершается неудачно, когда тип T отличается между экземплярами:

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>(); 
    }
}

Вывод:

1

2

3

1

Как только тип T переключается на int вместо строки, счетчик сбрасывается.

Не получается ли это по замыслу, и если да, то в чем причина или как я могу обойти это?Или это ошибка?В некоторой степени это имеет смысл, потому что тип T, будучи универсальным, находится в объявлении класса, но ..

Ответы [ 7 ]

32 голосов
/ 16 ноября 2011

Каждый отдельный T создает новый класс для A<T> и, следовательно, различные статические счетчики.

Чтобы обойти это, вы можете использовать наследование так:

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);      
   }
}
26 голосов
/ 16 ноября 2011

Не ошибка - это сделано специально и является следствием того, как работают дженерики.

Универсальный тип, такой как ваш A<T>, служит шаблоном - когда вы используете параметры типа, компилятор создает фактический класс с этим типом T, и для каждого другого типа T создается отдельный класс.

Это объясняет результаты, которые вы видите - есть статическое поле для A<int> и еще одно для A<string>.

10 голосов
/ 16 ноября 2011

Это связано с тем, что под капотом генерируются разные типы для классов с разными параметрами универсального типа Это различие только для параметров типа значения, как любезно отмечено Беном в комментарии.

Ознакомьтесь с этими статьями MSDN:

EDIT:

Рассмотрим следующий код:

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
    }
}
7 голосов
/ 16 ноября 2011

Универсальный класс - это шаблон, из которого создаются другие классы.A List<String> и List<int> - это два совершенно разных класса, несмотря на то, что оба они произошли от List<T>.

.Не помещайте статический класс в общий класс.Это приведет к тому, что статический класс будет сгенерирован для каждого значения 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++;
    }
}
5 голосов
/ 16 ноября 2011

Дженерики с разными типами параметров - это разные типы. Таким образом, A<int> и A<string> - это разные классы, поэтому им назначаются разные статики.

3 голосов
/ 16 ноября 2011

Это по замыслу.Экземпляр A<int> не является экземпляром A<string>, это разные классы, поэтому для каждого класса есть две статические переменные.

0 голосов
/ 17 сентября 2015

A<int> на самом деле другой класс, чем A<string>, следовательно, они имеют разные статические счетчики

Вот почему Resharper помечает статические переменные в дженериках, потому что очень немногие программисты, кажется, понимают статику и особенно статику в дженериках

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...