Как заставить использование фабрики на структуру в C # - PullRequest
3 голосов
/ 04 декабря 2008

У меня есть структура c #, где мне нужно запретить вызов конструктора no args.

MyStruct a;
/// init a by members   // OK
MyStruct b = MyStruct.Fact(args); // OK, inits by memebers

MyStruct s = new MyStruct(); // can't have that

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

В C ++ это было бы легко, добавить приватный конструктор, но c # этого не позволяет.

Есть ли способ предотвратить вышесказанное?

Мне действительно нужно принудительно использовать фабрику, чтобы запретить все публичные вызовы конструктора сработало бы так же.


Полное раскрытие: чтобы избежать монозависимости, приложение c # автоматически переводится в D, где new Struct() приводит к указателю, и это все портит мне. Однако этот вопрос актуален, несмотря на это, поэтому просто игнорируйте его.

Ответы [ 5 ]

11 голосов
/ 04 декабря 2008

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

Фактически, вы можете запретить это утверждение, если вы счастливы записать свой тип значения в IL. Я только что проверил, и если вы убедитесь, что ваш тип значения имеет только конструктор без параметров, и сделаете его внутренним, вы не сможете написать MyStruct ms = new MyStruct(); Но это не мешает:

MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];

, который работает вокруг нового ограничения - так что он на самом деле ничего вам не покупает. Это действительно хорошо, так как возиться с ИЛ было бы грязно.

Вы уверены, что действительно хотите писать структуру во-первых? Это почти никогда не хорошая идея.

3 голосов
/ 04 декабря 2008

Вы не можете.

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

Что именно вы пытаетесь достичь, делая это? Структуры являются типами значений, поэтому вы получите «новую» структуру для большинства операций. Будет очень трудно реализовать ограничения, для которых вы будете использовать фабрику в структуре.

2 голосов
/ 04 декабря 2008

Любой может создать структуру в любое время без вызова конструктора, если у него есть доступ к структуре. Подумайте об этом так:

Если вы создаете массив объектов с 1000 элементами, все они инициализируются нулем, поэтому конструкторы не вызываются.

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

Впрочем, ваша заводская идея хороша. Будет ли это соответствовать вашим потребностям - создать интерфейс и показать его, но сделать структуру частной или внутренней? Это примерно так близко, как вы получите.

0 голосов
/ 29 января 2016

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

Много шаблонного кода. Поскольку вы используете D, вы, возможно, думаете о том же, что и я: «Я бы хотел, чтобы в C # были шаблоны миксинов».

Пример:

using System;

namespace CrazyStruct
{
    public struct MyStruct
    {
        private readonly int _height;
        private readonly bool _init; // Will be 'false' using default(MyStruct).

        /// <summary>
        /// Height in centimeters.
        /// </summary>
        public int Height
        {
            get
            {
                if (!_init)
                {
                    // Alternatively, could have the preferred default value set here.
                    // _height = 200; // cm
                    // _heightInit = true;
                    throw new InvalidOperationException("Height has not been initialized.");
                }
                return _height;
            }
            // No set:  immutable-ish.
        }

        private MyStruct(int height)
        {
            _height = height;
            _init = true;
        }

        public static MyStruct Factory(int height)
        {
            return new MyStruct(height);
        }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            MyStruct my = MyStruct.Factory(195);
            Console.WriteLine("My height is {0} cm.", my.Height);
            try
            {
                var array = new MyStruct[1];
                var ms = array[0];
                Console.WriteLine("My height is not {0} cm.", ms.Height);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught the expected exception: {0}.", ex);
            }
            Console.ReadKey();
        }
    }
}
0 голосов
/ 04 декабря 2008

Поместите его в свою сборку и используйте MyStruct () без аргументов в качестве Internal (Friend in VB). Фабрика должна быть в той же сборке, что и MyStruct (), но с открытым средством доступа.

Теперь фабрика может обращаться к MyStruct без аргументов, но все, что вызывает извне сборки, должно использовать Factory.

Редактировать: Плохо, я не учел, что это структура. Вы не можете сделать это со структурой, только с классом - в этом случае мое предыдущее утверждение остается в силе.

...