Почему мои статические объекты не создаются, когда первый доступ к статическому классу является статическим методом в базовом классе? - PullRequest
2 голосов
/ 10 августа 2010

У меня есть следующий класс:

public class DocketType : Enumeration<DocketType, int, string>
{
    public static DocketType ChangeOver = new DocketType(1, "Changeover");
    public static DocketType Withdrawal = new DocketType(2, "Withdrawal");
    public static DocketType Installation = new DocketType(3, "Installation");

    private DocketType(int docketTypeId, string description) 
        : base(docketTypeId, description)
    {
    }
}

Со следующим базовым классом:

public abstract class Enumeration<TEnum, X, Y> : IComparable 
    where TEnum : Enumeration<TEnum, X, Y> 
    where X : IComparable 
    where Y : IComparable
{        
    protected Enumeration(X value, Y displayName)
    {
        AddToStaticCache(this);
    }
    public static TEnum Resolve(X value)
    {
        return Cache[value] as TEnum;
    }
}

У меня проблема в том, что Changeover, Withdrawal и Installationне создаются при первом использовании статического класса с помощью метода Resolve в базовом классе.Т.е. если я позвоню Resolve, тогда Cache будет пустым.

Однако, если я сделаю что-то вроде DocketType foo = DocketType.Changeover; в Application_Start, тогда все статические поля будут созданы, а затем Cacheимеет все три значения.

Как правильно создать эти статические поля, чтобы этот сценарий работал?

Ответы [ 2 ]

7 голосов
/ 10 августа 2010

Я не думаю, что поля в DocketType должны быть инициализированными, когда все, к чему вы обращаетесь - Enumeration<>.Вы вообще не ссылаетесь на тип DocketType, когда звоните Enumeration<>.Resolve().Должен ли CLR действительно инициализировать все подклассы каждый раз, когда вы обращаетесь к статическому методу или статическому полю?Это замедлит ваш код, и в большинстве случаев излишне.

Вы можете попробовать написать Docket.Resolve(), что позволяет вам делать на C #, но я не знаю, скомпилируется ли это в нечто иное, чем раньше;компилятор может просто превратить его в Enumeration<DocketType, int, string>.Resolve(), и вы вернетесь к квадратному.

Если честно, я склонен предположить, что ваша структура кода имеет недостатки, и проблема, с которой вы сталкиваетесь, заключается всимптом этого.Вам не нужно полагаться на Cache, содержащий что-то.Вам не нужно полагаться на инициализацию статического типа, которая произошла, когда вы не используете этот тип.

Поэтому вы можете выбрать следующие параметры:

  • Бессмысленно указывать наDocketType где-нибудь в вашем методе Main(), чтобы обеспечить инициализацию, и жить с идеей, что ваша структура кода может быть ошибочной.
  • Переместить статические поля в другой тип, возможно, сам Enumeration<>, которыйустраняет недостаток, но не решает его полностью.
  • Подумайте о фундаментальной структуре вашего кода и измените его структуру так, чтобы вам не пришлось полагаться на заполненный кеш.
2 голосов
/ 10 августа 2010

РЕДАКТИРОВАТЬ: я не понимал, что вы только когда-либо ссылались на базовый тип. Это определенно имеет проблемы - ничто не гарантирует запуск инициализатора типа для DocketType в этом случае. Я думал, что вы вызываете метод в DocketType, который затем использовал кеш.

В этом случае это тоже не сработало бы раньше. Использование типа в качестве аргумента универсального типа, насколько я знаю, не вызывает инициализацию типа, и это то, что вам нужно.

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

Я согласен с Тимви: я думаю, что вашим лучшим решением будет реструктуризация вашего дизайна, чтобы вам это не нужно.

...