4 Stimmen

Eine Art von Erstellungsmuster in C# benötigt

Ich habe den folgenden Typ:

// incomplete class definition
public class Person
{
    private string name;

    public string Name
    {
        get { return this.name; }
    }
}

Ich möchte, dass dieser Typ erstellt y aktualisiert mit einer Art dediziertem Controller/Builder, aber ich möchte, dass es schreibgeschützt für andere Typen .

Dieses Objekt muss auch jedes Mal ein Ereignis auslösen, wenn es von seinem Controller/Builder aktualisiert wird.

Zusammenfassend lässt sich sagen, dass nach der vorherigen Typdefinition Skelett :

  • El Person konnte nur von einem bestimmten Controller instanziiert werden
  • Dieser Controller könnte Update den Zustand der Person ( name Feld) zu jeder Zeit
  • El Person Sie müssen eine Meldung für den Rest der Welt, wenn sie auftritt
  • Alle anderen Typen sollten nur in der Lage sein lesen Person Eigenschaften

Wie soll ich das umsetzen? Ich spreche über einen Controller/Builder hier, aber alle anderen Lösungen sind willkommen.

Anmerkung: Ich würde mich auf die internal Modifikator, aber idealerweise sollten alle meine Sachen in derselben Baugruppe sein.

5voto

JasonTrue Punkte 18756

Erstellen Sie eine Schnittstelle IReadOnlyPerson, die nur get-Zugriffsmöglichkeiten bereitstellt. Lassen Sie Person IReadOnlyPerson implementieren. Speichern Sie den Verweis auf Person in Ihrem Controller. Geben Sie anderen Clients nur die ReadOnly-Version.

Dies schützt vor Fehlern, aber nicht vor Betrug, wie bei den meisten OO-Funktionen. Clients können zur Laufzeit auf Person casten, wenn sie zufällig wissen (oder vermuten), dass IReadOnlyPerson von Person implementiert wird.

Update, gemäß dem Kommentar:

Die Read Only-Schnittstelle kann wie jedes andere Objekt auch einen Ereignisdelegaten bereitstellen. Das Idiom, das im Allgemeinen in C# verwendet wird, verhindert nicht, dass Clients die Liste der Listener durcheinander bringen, aber die Konvention ist nur, Listener hinzuzufügen, also sollte das ausreichend sein. Innerhalb eines Set-Accessors oder einer Funktion mit zustandsändernden Seiteneffekten, rufen Sie einfach den Ereignisdelegaten mit einem Guard für den Null-Fall (keine Zuhörer) auf.

1voto

Jason Cohen Punkte 78227

Ich möchte eine schreibgeschützte Schnittstelle haben. Dann kann der Builder/Controller/was auch immer das Objekt direkt referenzieren, aber wenn Sie dieses Objekt nach außen hin offenlegen, zeigen Sie nur die Schnittstelle.

1voto

fryguybob Punkte 4280

Verwenden Sie eine Schnittstelle IPerson und eine verschachtelte Klasse:

public class Creator
{
    private class Person : IPerson
    {
        public string Name { get; set; }
    }

    public IPerson Create(...) ...

    public void Modify(IPerson person, ...)
    {
        Person dude = person as Person;
        if (dude == null)
            // wasn't created by this class.
        else
            // update the data.
    }
}

1voto

cfeduke Punkte 22720

Ich glaube internal ist der am wenigsten komplexe und beste Ansatz (dies erfordert natürlich mehrere Baugruppen). Kurz zu tun einige Overhead-intensive Stack Walking, um den Aufrufer in der Eigenschaft Setter zu bestimmen, könnten Sie versuchen:

interface IPerson 
{
    Name { get; set; } 
}

und implementieren diese Schnittstelle explizit:

class Person : IPerson 
{
    Name { get; private set; }
    string IPerson.Name { get { return Name; } set { Name = value; } } 
}

dann führen Sie explizite Schnittstellen-Casts in Ihrem Builder durch, um Eigenschaften zu setzen. Dies schützt Ihre Implementierung immer noch nicht und ist keine gute Lösung, auch wenn es Ihre Absicht etwas unterstreicht.

In Ihren Property Settern müssen Sie eine Ereignisbenachrichtigung implementieren. Annäherung an dieses Problem selbst würde ich nicht erstellen Sie separate Ereignisse und Event-Handler für jede Eigenschaft, sondern erstellen Sie eine einzelne PropertyChanged-Ereignis und feuern Sie es in jeder Eigenschaft, wenn eine Änderung auftritt (wo die Event-Argumente würde die Eigenschaft Name, alten Wert und neuen Wert enthalten).

1voto

Es scheint seltsam, dass ich zwar den Namen des Person-Objekts nicht ändern kann, aber ich kann einfach seinen Controller greifen und ihn dort ändern. Das ist kein guter Weg, um die Daten eines Objekts zu sichern.

Aber nichtsdestotrotz, hier ist eine Möglichkeit, es zu tun:

    /// <summary>
    /// A controlled person.  Not production worthy code.
    /// </summary>
    public class Person
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            private set
            {
                _name = value;
                OnNameChanged();
            }
        }
        /// <summary>
        /// This person's controller
        /// </summary>
        public PersonController Controller
        {
            get { return _controller ?? (_controller = new PersonController(this)); }
        }
        private PersonController _controller;

        /// <summary>
        /// Fires when <seealso cref="Name"/> changes.  Go get the new name yourself.
        /// </summary>
        public event EventHandler NameChanged;

        private void OnNameChanged()
        {
            if (NameChanged != null)
                NameChanged(this, EventArgs.Empty);
        }

        /// <summary>
        /// A Person controller.
        /// </summary>
        public class PersonController
        {
            Person _slave;
            public PersonController(Person slave)
            {
                _slave = slave;
            }
            /// <summary>
            /// Sets the name on the controlled person.
            /// </summary>
            /// <param name="name">The name to set.</param>
            public void SetName(string name) { _slave.Name = name; }
        }
    }

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