5 Stimmen

Wie erzwinge ich mit einer Fabrik auf eine Struktur in C #

Ich habe eine c#-Struktur, wo ich den Aufruf der keine Args-Konstruktor auf es zu verbieten müssen.

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

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

Ich tue dies hauptsächlich, um explizite Werte für alle Mitglieder zu erzwingen, da es keine gültigen Standardwerte gibt und alle Mitglieder gültige Werte haben müssen.

In C++ wäre es einfach, einen privaten Konstruktor hinzuzufügen, aber c# erlaubt das nicht.

Gibt es eine Möglichkeit, dies zu verhindern?

Ich muss wirklich die Verwendung einer Fabrik erzwingen, so dass die Verhinderung aller öffentlichen Konstruktoraufrufe genauso gut funktionieren würde.


Vollständige Offenlegung: Um eine Mono-Abhängigkeit zu vermeiden, wird die c#-Anwendung automatisch nach D übersetzt, wo new Struct() führt zu einem Zeiger, und das bringt die Sache für mich durcheinander. Aber diese Frage ist trotzdem relevant, also ignorieren Sie es einfach.

0 Stimmen

Mir gefällt die Idee einer "öffentlichen Schranke" - vielleicht als Strafe für obskure Verbrechen?

14voto

Jon Skeet Punkte 1325502

Das können Sie nicht. Alle structs haben einen öffentlichen parameterlosen Konstruktor per Definition in C#. (In der CLR fast keine von ihnen tun, aber sie können immer handeln, als ob sie haben.) Siehe diese Frage warum Sie nicht Ihre eigenen parameterlosen Konstruktoren für Structs definieren können (jedenfalls in C#).

In der Tat, Sie kann vermeiden Sie diese Anweisung, wenn Sie Ihren Wertetyp gerne in AWL schreiben möchten. Ich habe es gerade überprüft, und wenn Sie sicherstellen, dass Ihr Wertetyp nur einen parameterlosen Konstruktor hat, und ihn intern machen, können Sie nicht schreiben MyStruct ms = new MyStruct(); Aber das ist kein Hindernis:

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

die die neue Einschränkung umgeht - man kann sich also nicht wirklich etwas kaufen. Das ist eigentlich ganz gut, denn in IL herumzuwurschteln wäre chaotisch.

Sind Sie sicher, dass Sie überhaupt eine Struktur schreiben wollen? Das ist presque nie eine gute Idee.

3voto

MrKurt Punkte 5050

Das können Sie nicht.

Alle Werte in einer Struktur müssen zur Konstruktionszeit initialisiert werden, und es gibt keine Möglichkeit, das außerhalb des Konstruktors zu tun.

Was genau wollen Sie damit erreichen? Structs sind Werttypen, daher erhalten Sie für die meisten Operationen eine "neue" Struktur. Es wird sehr schwierig sein, die Art von Einschränkungen zu erzwingen, für die Sie eine Fabrik auf eine Struktur verwenden würden.

2voto

Neil Punkte 6947

Jeder kann jederzeit ein struct erstellen, ohne einen Konstruktor aufzurufen, solange er Zugriff auf das struct hat. Stellen Sie sich das so vor:

Wenn Sie ein Array von Objekten mit 1000 Elementen erstellen, werden sie alle auf null initialisiert, so dass keine Konstruktoren aufgerufen werden.

Bei Structs gibt es so etwas wie Null nicht. Wenn Sie ein Array mit 1000 DateTime-Objekten erstellen, werden sie alle mit Null initialisiert, was DateTime.Min entspricht. Die Entwickler der Laufzeitumgebung haben sich dafür entschieden, dass Sie ein Array von Structs erstellen können, ohne den Konstruktor N-mal aufzurufen - ein Leistungseinbruch, den viele Leute nicht bemerken würden.

Die Idee mit der Fabrik ist aber gut. Würde es Ihren Bedürfnissen entsprechen, eine Schnittstelle zu erstellen und diese freizulegen, aber die Struktur privat oder intern zu machen? Näher werden Sie nicht herankommen.

0voto

BenAlabaster Punkte 37617

Legen Sie es in eine eigene Assembly und haben Sie die MyStruct() ohne die Args als Internal (Friend in VB). Legen Sie die Factory in dieselbe Assembly wie MyStruct(), aber mit einem Public Accessor.

Jetzt kann die Factory auf die no args MyStruct zugreifen, aber alle Aufrufe von außerhalb der Assembly müssen die Factory verwenden.

Edit: Mein Fehler, ich habe nicht bedacht, dass dies eine Struktur ist. Sie können dies nicht mit einer Struktur tun, nur mit einer Klasse - in diesem Fall, meine vorherige Aussage steht.

0voto

Eljay Punkte 3901

Sie könnten eine Struktur erstellen, die erkennt, ob sie sich in einem standardmäßig initialisierten Zustand befindet, und dann in diesem Fall etwas Passendes tun. Ich habe die Factory drin gelassen, aber ein Konstruktor könnte im allgemeinen, einfachen Fall auch eine angemessene Factory sein.

Es handelt sich um eine Menge Standardcode. Da Sie D verwenden, denken Sie vielleicht dasselbe wie ich: "Ich wünschte, C# hätte Template-Mixins."

Beispiel:

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();
        }
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X