Muster für das Erstellen eines Einfachen und Effizienten Wert-Typ

Motivation:

In der Lesung von Mark Seemann blog auf Code Smell: Automatische Eigenschaft sagt er gegen Ende:

Die Quintessenz ist, dass die automatische Eigenschaften sind selten angemessen.
In der Tat, Sie sind nur geeignet, wenn der Typ der Eigenschaft ist ein
Wert geben und alle erdenklichen Werte erlaubt sind.

Er gibt int Temperature als ein Beispiel, einen schlechten Geruch und schlägt die beste fix ist, Gerät bestimmten Wert geben wie Celsius. Also habe ich beschlossen, zu versuchen, schreiben Sie eine benutzerdefinierte Celsius-Wert geben, kapselt alle bounds checking und Typ-Konvertierung Logik als eine übung in mehr SOLID.

Grundlegende Anforderungen:

  1. Unmöglich, einen ungültigen Wert
  2. Kapselt die Konvertierung Operationen
  3. Effient Bewältigung (äquivalent zu int its austauschen)
  4. So intuitiv zu benutzen wie möglich (versuche, die für die Semantik eines int)

Umsetzung:

[System.Diagnostics.DebuggerDisplay("{m_value}")]
public struct Celsius //: IComparable, IFormattable, etc...
{
    private int m_value;

    public static readonly Celsius MinValue = new Celsius() { m_value = -273 };           //absolute zero
    public static readonly Celsius MaxValue = new Celsius() { m_value = int.MaxValue };

    private Celsius(int temp)
    {
        if (temp < Celsius.MinValue)
            throw new ArgumentOutOfRangeException("temp", "Value cannot be less then Celsius.MinValue (absolute zero)");
        if (temp > Celsius.MaxValue)
            throw new ArgumentOutOfRangeException("temp", "Value cannot be more then Celsius.MaxValue");

        m_value = temp;
    }

    public static implicit operator Celsius(int temp)
    {
        return new Celsius(temp);
    }

    public static implicit operator int(Celsius c)
    {
        return c.m_value;
    }

    //operators for other numeric types...

    public override string ToString()
    {
        return m_value.ToString();
    }

    //override Equals, HashCode, etc...
}

Tests:

[TestClass]
public class TestCelsius
{
    [TestMethod]
    public void QuickTest()
    {
        Celsius c = 41;             
        Celsius c2 = c;
        int temp = c2;              
        Assert.AreEqual(41, temp);
        Assert.AreEqual("41", c.ToString());
    }

    [TestMethod]
    public void OutOfRangeTest()
    {
        try
        {
            Celsius c = -300;
            Assert.Fail("Should not be able to assign -300");
        }
        catch (ArgumentOutOfRangeException)
        {
            //pass
        }
        catch (Exception)
        {
            Assert.Fail("Threw wrong exception");
        }
    }
}

Fragen:

  • Ist es ein Weg, um MinValue/MaxValue const statt readonly? Blick auf die BCL mag ich, wie die meta-Daten, die definition von int klar MaxValue und MinValue als compile-Zeit-Konstanten. Wie kann ich imitieren? Ich sehe nicht ein Weg, um eine Celsius-Objekt ohne Aufruf des Konstruktors oder aussetzen der Implementierung detail, Celsius speichert einen int.
  • Bin ich, fehlt jede usability-features?
  • Gibt es ein besseres Muster für die Erstellung einer benutzerdefinierten einzigen Feld Wert geben?
  • Überprüfen Sie heraus diese Frage (somewnat beantworten Sie "fehlende usability-features" - Teil) - stackoverflow.com/questions/441309/why-are-mutable-structs-evil und links aus Ihr heraus. Nützlich für alle value-Typen.
  • +1 für die Frage, auf immer SOLIDE.
  • Ich habe gelesen, alle die "mutable structs sind böse" - posts vor. Ich bin damit einverstanden. Das problem ist, dass wenn ich das private Feld readonly dann gebrannt.MaxValue ruft der Konstruktor erfordert Celsius.MaxValue-bereits definiert. Diese ist kreisförmig und führt zu einer runtime exception. Das ist, warum ich bin mit einem Standard-Konstruktor in der MaxValue-definition. Kennst du eine Möglichkeit, um dieses? Einen besonderen Zweck "nicht überprüfen bounds" privaten Konstruktor fühlt sich falsch an.
  • Ich wusste nicht, dass. Ich denke, dass spezielle Methode (private CreateConstantValue()?) das schafft Konstanten, die für den gegebenen Typ wäre nützlich für die selbst-Dokumentation der code - den code anzuschauen, wie es jetzt ist gibt es keine Möglichkeit zu wissen, warum Sie zu rufen Standard-Konstruktor.
InformationsquelleAutor ErnieL | 2011-11-07
Schreibe einen Kommentar