Как обеспечить использование фабрики в структуре в C#

StackOverflow https://stackoverflow.com/questions/341957

  •  19-08-2019
  •  | 
  •  

Вопрос

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

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# этого не позволяет.

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

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


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

Это было полезно?

Решение

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

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

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

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

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

Другие советы

Вы не можете.

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

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

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

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

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

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

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

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

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

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

Это много шаблонного кода.Поскольку вы используете 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();
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top