2016-08-17 4 views
0

Рассмотрим этот код:типоспецифичной статическое состояние в общих классах

public class Person 
{ 
    public string Name { get; set; } 
} 

public class Animal 
{ 
    public string Name { get; set; } 
} 

public interface IHandler<T> 
{ 
    T Handle(T eventData); 
} 

public class UpdatePersonHandler : IHandler<Person> 
{ 
    public Person Handle(Person eventData) 
    { 
     var test = eventData.Name; 
     return eventData; 
    } 
} 
public class UpdatePersonHandler2 : IHandler<Person> 
{ 
    public Person Handle(Person eventData) 
    { 
     var test = eventData.Name; 
     return eventData; 
    } 
} 
public class UpdateAnimalHandler : IHandler<Animal> 
{ 
    public Animal Handle(Animal eventData) 
    { 
     var test = eventData.Name; 
     return eventData; 
    } 
} 

public class Bus<T> 
{ 
    public static readonly IList<IHandler<T>> Handlers = new List<IHandler<T>>(); 

    public static void Register(IHandler<T> handler) 
    { 
     if (handler != null) 
      Handlers.Add(handler); 
    } 

    public static void Raise(T eventData) 
    { 
     foreach (var handler in Handlers) 
     { 
      handler.Handle(eventData); 
     } 
    } 
} 

и тестовый код:

[TestMethod] 
public void TestRegister() 
{ 
    Bus<Person>.Register(new UpdatePersonHandler()); 
    Bus<Person>.Register(new UpdatePersonHandler()); 
    Bus<Person>.Register(new UpdatePersonHandler2()); 

    Bus<Animal>.Register(new UpdateAnimalHandler()); 

    Debug.Print(Bus<Person>.Handlers.Count.ToString()); 
    Debug.Print(Bus<Animal>.Handlers.Count.ToString()); 
} 

Выход этого теста:

3 
1 

Что здесь?

Похоже, что Framework обновляет класс шины для каждого типа, который представляется ему через метод регистрации static. Для этого он должен был вызвать конструктор по умолчанию на Bus<T> для каждого нового типа.

Но почему? Как это работает?

Имеет ли это какую-либо практическую полезность, или это просто интересное, но неясное любопытство C#, которого следует избегать в производственном коде?

+0

Разговор [переехал в чат] (http://chat.stackoverflow.com/rooms/121216/discussion-between-robert-harvey-and-peter-duniho). –

ответ

3

Да, каждый другой тип, переданный статическому классу Bus<T>, приводит к вызову вызываемого конструктора по умолчанию. Простой способ проверить это, чтобы дать ему конструктор по умолчанию:

static Bus(){ Debug.Print("ctor"); } 

Используя это производит вывод

ctor 
ctor 
3 
1 

Причина этого заключается в том, что общие классы являются лишь шаблоны для классов, и это все еще верно для статических классов. Как только общий тип был присвоен классу Bus, тогда шаблон материализуется в класс, а именно, когда вызывается конструктор.

Этот материализованный класс уникален для других универсальных типов того же класса. Итак, когда используются Bus<Person> и Bus<Animal>, это фактически отдельные классы, имеют отдельные статические данные элемента и требуют отдельного экземпляра.

Что касается использования и перспективы статических полей с общей типизации, есть MSDN предупреждение (по-видимому, Resharper бы поднял это)

CA1000: Не объявлять статические члены на общих типов
https://msdn.microsoft.com/en-us/library/ms182139(VS.80).aspx

+1

Microsoft, похоже, предположила, что проблема с этой практикой заключается в том, что она выводит тип вывода. Множественные экземпляры класса (по одному для каждого типа), похоже, не беспокоят их вообще. «По-дизайн», как это было. –

+0

@RobertHarvey: _ «Множественные экземпляры класса (по одному для каждого типа), похоже, не беспокоят их вообще» _ - точно так же и не должны. И даже проблема с выводом типа является спорной; если в конце дня вам понадобится статический общий класс или общий метод без каких-либо типично типизированных параметров, это то, что вам нужно сделать. Это происходит (один пример - вспомогательные методы для работы с типами enum). Обратите внимание, что методы расширения облегчают боль в таких сценариях. –

+0

Ницца, я получаю его сейчас – ShaneKm