Инициализация переменной-члена только для чтения с конструктором по умолчанию в C # - PullRequest
0 голосов
/ 01 июня 2011

У меня есть простая реализация Factory в моем коде. Я стремлюсь, чтобы сконструированные объекты сохранили ссылку на фабрику, которая их создала, поэтому я подключил их примерно так:

public class Factory {

    public T CreateObject<T>() where T : Foo, new() 
    {
        return new T() {
            parent = this
        };
    }
}

public class Foo {

    internal Factory parent;
    internal Foo() { }
}

Это работает во время чтения, но я подумал, что, вероятно, мне нужна эта переменная parent, чтобы ее нельзя было изменить, если она установлена ​​на заводе. Однако, если я объявлю это как internal readonly Factory parent;, то фабрика не сможет больше установить его значение при строительстве.

Обычно я исправляю это, предоставляя параметризованный конструктор, но это убивает универсальную реализацию, потому что AFAIK where T : new() подразумевает конструктор без параметров.

Может быть, мне просто не хватает нескольких шестеренок с моими чипами на C #, но как лучше всего реализовать что-то подобное? (Или было бы лучше просто отказаться от readonly и поверить, что код не будет модифицировать parent небезопасным образом - на ум придет NullReferenceException ---?)

Ответы [ 3 ]

4 голосов
/ 01 июня 2011

Я не думаю, что вы можете получить именно то, что вы хотите, но вы можете создать свойство, которое можно назначить только один раз, и позволить фабрике перезаписать его. Но я думаю, что пользователь всегда может принудительно изменить значение с помощью new CreatingFactory, но я думаю, что это затрудняет и, по крайней мере, ясно показывает ваше намерение.

Вы можете сделать что-то вроде ниже.

    class Foo
    {
        private Factory factory;

        public Factory CreatingFactory
        {
            get { return factory; }
            set
            {
                if (factory != null)
                {
                    throw new InvalidOperationException("the factory can only be set once");
                }
                factory = value;
            }
        }
    }

    class Factory
    {
        public T Create<T>()
            where T : Foo, new()
        {
            T t = new T()
            {
                CreatingFactory = this
            };
            return t;
        }
    }

После создания я искал и нашел ответ, который, вероятно, лучше моего: Есть ли способ установить свойство один раз только в C #

4 голосов
/ 01 июня 2011

Вы можете использовать отражение для установки поля, это будет работать независимо от модификатора readonly:

public class Factory
{
    public T CreateObject<T>() where T : Foo, new()
    {
        T t = new T();
        t.GetType()
         .GetField("parent", BindingFlags.NonPublic | BindingFlags.Instance)
         .SetValue(t, this);
        return t;
    }
}
0 голосов
/ 01 июня 2011

Я не уверен, что это именно то, что вам нужно, но я предложил подход, который заменяет ваш параметр универсального типа на Func, который показывает, как построить объект Foo, и позволяет вам установить родитель в конструкторе.

public class Factory {
    private Func<Factory, Foo> creator;

    public Factory(Func<Factory, Foo> creator) {
        this.creator = creator;
    }

    public Foo CreateObject()
    {
        return this.creator(this);
    }
}

public class Foo {
    internal readonly Factory parent;
    internal Foo(Factory parent) {
        this.parent = parent;
    }
}

, а затем

public void Main() {
    Factory myfactory = new Factory(fact => new Foo(fact));
    Foo myfoo = myfactory.CreateObject();
}
...