Frage

Nehmen wir an, ich habe ein Datenobjekt, aber dieses Objekt kann eine von verschiedenen Arten von Daten enthalten.

class Foo
{
    int intFoo;
    double doubleFoo;
    string stringFoo;
}

Jetzt möchte ich einen Accessor erstellen. Eine Möglichkeit, diese Daten zu erreichen. Offensichtlich könnte ich mehrere Zubehör erstellen:

public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();

Oder ich könnte mehrere Eigenschaften erstellen

public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }

Ich nicht, dass das ein sehr gutes Design ist. Es ist erforderlich, dass der Client -Code mehr über den Typ besorgt ist, als er sein sollte. Darüber hinaus brauche ich wirklich nur einen einzelnen Wert für diese Klasse, und das obige würde es einem der einzelnen Typen gleichzeitig ermöglichen. Nicht gut.

Eine Option ist die Verwendung von Generika.

class Foo<T>
{
    public T TheFoo { get; set; }
}

Dies schafft jedoch kein Foo, es schafft ein Foou003CT> . Ein anderer Typ für jeden, daher kann ich sie nicht wirklich als den gleichen Typ verwenden.

Ich könnte Foo ableitenu003CT> Behandeln Sie dann alle von Foobase als Foobase, aber dann bin ich wieder im Problem des Zugriffs auf die Daten.

Eine andere Option Generika besteht darin, so etwas zu verwenden:

class Foo
{
    string stringRepresentationOfFoo;
    public T GetFoo<T>() { return /* code to convert string to type */ }
}

Natürlich ist das Problem, dass jede Art von T bestanden werden könnte, und ehrlich gesagt ist es ein bisschen beschäftigt.

Ich könnte auch nur die Werte fällen und ein Objekt zurückgeben, aber dann gibt es keine Art Sicherheit.

Im Idealfall möchte ich alle gleich behandeln, aber ich möchte Sicherheit tippen, damit ich, wenn es kein Stringfoo gibt, nicht einmal eine Referenz auf ein Stringfoo kompilieren kann.

Foo foo = new Foo("Foo");
string sFoo = foo.Value;  // succeeds.
&nbsp;
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error

Vielleicht ist das nicht einmal möglich ... und ich muss ein paar Kompromisse machen, aber vielleicht fehlt mir etwas.

Irgendwelche Ideen?

BEARBEITEN:

Ok, wie Daniel betont, ist die Kompilierzeitüberprüfung eines Laufzeittyps nicht praktisch.

Was ist meine beste Option, um das zu tun, was ich hier tun möchte? Behandeln Sie nämlich alle gleich, haben aber immer noch einen relativ vernünftigen Zugangsmechanismus?

Edit2:

Ich möchte den Wert nicht in verschiedene Typen konvertieren. Ich möchte den richtigen Typ für den Wert zurückgeben. Das heißt, wenn es ein Doppel ist, möchte ich keine int zurückgeben.

War es hilfreich?

Lösung

Wie wäre es, wenn Sie die Variable als Parameter für das Get eingeben? So was:

int i = foo.get(i);

Dann hätten Sie in Ihrer Klasse so etwas wie:

public int get(int p) {
    if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
    return this.intVal;
}

public float get(float p) {
    if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
    return this.floatVal;
}

Diese Art von The Type Checking Inside Out: Anstatt zu überprüfen, welchen Typ Foo hält, überprüfen Sie Foo, welchen Typ Sie möchten. Wenn es Ihnen diesen Typ geben kann, erledigt es, oder es wird eine Laufzeitausnahme ausgelöst.

Andere Tipps

Ich glaube nicht, dass dies funktionieren könnte (geben Sie den Compiler -Fehler, den Sie möchten)

Was möchten Sie, dass dies tun:

Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;

Ist das ein Compiler -Fehler?

Ich denke, "behandeln Sie sie alle gleich" (zumindest so wie Sie es beschrieben haben) und "Kompilierzeitfehler" werden sich gegenseitig ausschließen.

In jedem Fall denke ich, dass der "beste Weg" ein Kompromiss zwischen Generika und Vererbung sein wird. Sie können a definieren Foo<T> Das ist eine Unterklasse von Foo; Dann können Sie noch Sammlungen von haben Foo.

abstract public class Foo
{
  // Common implementation

  abstract public object ObjectValue { get; }
}

public class Foo<T> : Foo
{
   public Foo(T initialValue)
   {
      Value = initialValue;
   }

   public T Value { get; set; }

   public object ObjectValue
   { 
      get { return Value; }
   }
}

Viele Systeme verwenden Helfermethoden, um die alternativen Typen zurückzugeben, genau wie das Basisobjekt von .NET Frameworks die Methode toString () hat

Wählen Sie aus, welches der beste Basistyp für jedes Ihrer Objekte ist, und geben Sie Methoden für andere Fälle an

z.B

class Foo{
    public Int32 Value { get; set; }
    public Byte ToByte() { return Convert.ToByte(Value); }
    public Double ToDouble() { return (Double)Value; }
    public new String ToString() { return Value.ToString("#,###"); }
}

Eine Sache ist es, jeden Typ in Ihrem internen Zustand der Klasse zu speichern, und ein anderer ist es, sie extern aufzudecken. Wenn Sie eine Klasse schreiben, deklarieren Sie tatsächlich einen Vertrag für ihr Verhalten. Die Art und Weise, wie Sie es schreiben, wird stark beeinflussen, wie der Clientcode bei der Verwendung der Klasse aussieht.

Zum Beispiel durch Implementierung der Ikonvertierbar Schnittstelle Sie geben an, dass Ihr Typ als äquivalenter Wert in einen beliebigen CLR -Typ konvertiert werden kann.

Ich habe auch Implementierungen gesehen, bei denen eine Wertklasse verwendet wurde, um die Ergebnisse von Berechnungen zu speichern, die entweder eine Zeichenfolge, doppelte, int oder boolesche darstellen könnten. Das Problem war jedoch, dass der Client -Code eine Eigenschaft des Werts eines Enum {String, Integer, Double, Boolean} überprüfen musste und dann entweder die Wertschöpfungseigenschaft gegossen hat (die von der Wertklasse extern als Objekttyp ausgesetzt wurde ) oder verwenden Sie die spezifischen Wertsträger, ValuedoUble, ValueInt, ValueBoolean Getters.

Warum nicht einfach benutzen string, double und int?


Nach Informationen über die Sammlung: Was ist mit der Verwendung object? Sie müssen sowieso danach nach Typen und ähnlichem suchen. Und um Ihnen dabei zu helfen, können Sie die verwenden is und as Betreiber. Und die Aufzählbare.cast -Methode, oder noch besser, die Aufzählbare Methode.

Was ist der Zweck dieser Klasse? Das größte Problem scheint das Design zu sein, das mindestens SRP brechen (Prinzip der Einzelverantwortung).

Wenn ich es jedoch richtig lese, möchten Sie jedoch einen Wert im Container speichern, den Container an den Client übergeben und den Wert typisch abrufen.

Mit diesem Ansatz können Sie Ihren Vorschlag verwenden, dh

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        Foo a = new Foo();
        a.SetValue(4);
        Console.WriteLine(a.GetValue<int>());

        Foo b = new Foo();
        a.SetValue("String");
        Console.WriteLine(a.GetValue<string>());

        Console.ReadLine();

        return 0;
    }
}


class Foo {
    private object value; // watch out for boxing here!
    public void SetValue(object value) {
        this.value = value;
    }

    public T GetValue<T>() {
        object val = this.value;
        if (val == null) { return default(T); } // or throw if you prefer
        try {
            return (T)val;
        }
        catch (Exception) {
            return default(T);
            // cast failed, return default(T) or throw
        }
    }
}
}

In diesem Fall geben Sie jedoch nicht einfach Daten als Objekt und alleine übergeben?

Abhängig von Ihren Bedürfnissen können Sie auch "PHP in C#" versuchen:

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        MyInt a = 1;
        MyInt b = "2";

        Console.WriteLine(a + b); // writes 3

        Console.ReadLine();

        return 0;
    }
}


class MyInt {
    private int value;

    public static implicit operator int(MyInt container) {
        return container.value;
    }

    public static implicit operator MyInt(int value) {
        MyInt myInt = new MyInt();
        myInt.value = value;
        return myInt ;
    }

    public static implicit operator MyInt(string stringedInt) {
        MyInt myInt = new MyInt();
        myInt.value = int.Parse(stringedInt);
        return myInt;
    }
}
}

Es tut mir leid, ich kaufe deine Prämisse einfach nicht. Wenn die Daten alle den gleichen Zweck haben, sollten sie alle den gleichen Typ haben. Betrachten Sie eine Klasse, die die aktuelle Temperatur halten soll, die von einem von mehreren Webdiensten zurückgegeben wird. Alle Dienstleistungen geben die Temperatur in Celsius zurück. Aber man kehrt als int zurück, man kehrt als Doppel zurück und man kehrt es als Zeichenfolge zurück.

Es sind nicht drei verschiedene Typen - es ist ein Typ - Double. Sie müssten lediglich die Nicht-Double-Rückkehr in Doppel konvertieren, was die Temperatur ist (oder möglicherweise schwimmt).

Wenn Sie im Allgemeinen mehrere Darstellungen von einer Sache haben, ist es immer noch eine Sache, nicht mehrere Dinge. Konvertieren Sie die mehreren Darstellungen in eine.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top