Как я могу получить доступ к статическому свойству в подклассе, когда свойство находится в базовом классе? - PullRequest
2 голосов
/ 15 июня 2009

Допустим, у меня есть:

public class Fruit
{

    public static List<String> Suppliers { get; protected set; }

    static Fruit()
    {
        Suppliers = new List<String>();
        Suppliers.Add("Company A");
    }

}

public class Banana : Fruit
{

    static Banana()
    {
        Suppliers.Add("Company B");
    }

}

Если я просто сделаю это в коде вызова:

foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

Я получаю:

  • Компания A

Принимая во внимание, что если я сделаю:

Banana b = new Banana();
foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

Я получаю (желаемый результат):

  • Компания A
  • Компания B

Edit: Прочитав ответы, я понимаю, что это не сработает.

В моем производственном коде мне нужен список значений, который является общим для типа объекта, и я хочу динамически добавлять различные значения в этот список строк на основе подтипа. (Контекст - LDAP - все записи имеют objectClass = top, а все пользовательские объекты имеют objectClass = user, top, organizationPerson, person). Угадайте, я должен использовать интерфейс или разные списки в каждом подклассе или что-то, если у кого-то нет лучшего предложения?

Ответы [ 5 ]

13 голосов
/ 15 июня 2009

С одной стороны, доступ к Banana.Suppliers вводит в заблуждение. Это всегда даст тот же результат, что и при доступе к Apple.Suppliers и т. Д. - у вас есть одна коллекция поставщиков.

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

Причина, по которой вы видите поставщиков, добавленных в статический конструктор для бананов после того, как вы создали банан, заключается в том, что он заставляет статический конструктор работать. Вы можете сделать что-нибудь еще, что заставит статический инициализатор работать, и вы получите те же результаты. Одним из примеров будет вызов статического метода внутри самого Banana.

Теперь я сильно подозреваю, что у вас есть серьезная проблема в том, что вы будете использовать одинаковых поставщиков для всех типов. Очевидно, это не ваш настоящий код, и наилучшее решение будет зависеть от того, что вы хотите, чтобы ваш настоящий код делал. Generics может эффективно дать вам статические переменные «по типу», используя аргументы типа: Foo<Banana>.StaticProperty и Foo<Apple>.StaticProperty действительно будут разными, предполагая, что StaticProperty объявлен в Foo<T>.

РЕДАКТИРОВАТЬ: Что касается вашего редактирования, я бы посоветовал избегать использования статики здесь. Возможно создать фабрику для каждого типа (реализуя интерфейс, который может быть универсальным). Обратите внимание, что вы вполне можете избежать создания отдельной фабрики type для каждого подтипа, если сможете создать соответствующий экземпляр со всеми соответствующими элементами для каждого типа.

Нам действительно нужно увидеть больше примеров, чтобы сказать наверняка, но в целом я считаю, что чем меньше у вас статических данных, тем более тестируемым будет ваш дизайн и тем меньше вы столкнетесь с такими проблемами :)

6 голосов
/ 15 июня 2009

Результаты, которые вы видите, вызваны тем, как работают статические конструкторы. CLR на самом деле не выполняет статический конструктор, когда используется первый экземпляр, поэтому вы получите желаемые результаты только во втором примере. См. MSDN для получения дополнительной информации.

2 голосов
/ 15 июня 2009

Дело в том, что это довольно легко объяснить, на самом деле. Когда вы получаете Banana.Suppliers, вы на самом деле просто ссылаетесь на Fruit.Suppliers - в этом случае компилятор завершает преобразование в класс Fruit из-за способа наследования (в классе Banana ничего не определено. Таким образом, ваш статический конструктор для Banana не вызывается в первом примере, потому что вы технически еще не ссылались на класс каким-либо образом. Поэтому, конечно, вы пропускаете элемент «Компания B» в первом результате.

Проблема здесь - это проблема дизайна. Я не совсем уверен, какие у вас намерения, но если вы действительно хотите, чтобы свойство в классе Fruit сохраняло список всех поставщиков, то вам нужно полностью инициализировать список в статический конструктор для класса Fruit. В целом, однако, я бы подумал, что для этой цели вам нужен отдельный класс набора данных или что-то подобное. Статические свойства, вероятно, не являются подходом к этой конструктивной особенности.

1 голос
/ 15 июня 2009

Доступ к Banana.Suppliers компилируется для доступа Fruit.Suppliers ..., что означает, что фактически ваш код фактически не касается класса Banana, что означает, что .NET не имеет оснований для выполнения статического конструктора Banana.

Если вы сделали что-то еще с классом Banana (например, создали его экземпляр), статический конструктор Banana запустится.

0 голосов
/ 15 июня 2009

Статический конструктор не ведет себя как конструктор экземпляра (они не вызываются явно). Вы должны получить доступ к свойству класса Banana, прежде чем оно будет построено. Вы пытаетесь применить некоторые принципы объектной ориентации к статическому поведению на классах. Они не могут быть равноправными, и выполнение этого приведет вас к определенному маршруту, которое в конечном итоге приведет вас к отчаянию.

Этот код:

foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

Точный эквивалент этого кода:

foreach(String supplier in Fruit.Suppliers)
    Console.WriteLine(supplier);

Так что статический конструктор на Banana никогда не вызывается, потому что он никогда не нужен. Приведенный ниже код демонстрирует, как при вызове статического члена fruit вызывается его статический конструктор, что дает искомые результаты.

public class Banana : Fruit
{
    static Banana()
    {
        Suppliers.Add("Company B");
    }
    public static void Foo()
    {

    }
}

// ...
Banana.Foo();
foreach (var supplier in Banana.Suppliers)
    Console.WriteLine(supplier);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...