Подтвердить что ты не робот

Почему эта статическая переменная не увеличивается при использовании дженериков?

Мне нужен определенный класс, чтобы содержать статический член, который отслеживает каждый раз экземпляр экземпляра этого класса, по сути, так, чтобы каждый экземпляр класса имел уникальный индекс. Он работает с не-общим классом, но эта обобщенная реализация не выполняется всякий раз, когда тип 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, являющийся общим, находится в объявлении класса, но..

4b9b3361

Ответ 1

Каждый другой 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);      
   }
}

Ответ 2

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

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

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

Ответ 3

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

Ознакомьтесь с этими статьями 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
    }
}

Ответ 4

Общий класс - это шаблон, из которого создаются другие классы. A List<String> и a 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

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

Ответ 6

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

Ответ 7

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

Вот почему Resharper помещает статические переменные в generics, потому что так мало программистов, похоже, понимают статику и особенно статику в generics