2 Stimmen

Arbeiten mit einer unbekannten Anzahl von unbekannten Typen - .NET

Hallo Leute, ich habe einige der Komplexitäten meiner Bedürfnisse auf den Kern dessen reduziert, was ich wissen muss.

Ich möchte eine Sammlung von Werten an eine Methode senden, und in dieser Methode möchte ich den Wert z. B. mit einer Eigenschaft einer Entität vergleichen. Die Eigenschaft wird immer vom gleichen Typ sein wie der Wert.

Ich möchte auch testen, ob der Wert null oder der Standardwert ist, natürlich abhängig davon, ob der Werttyp ein Referenztyp oder ein Werttyp ist.

Wenn alle Werte, die an die Methode gesendet werden, vom gleichen Typ sind, könnte ich dies ganz einfach mit Hilfe von Generika tun, etwa so:

public static void testGenerics<TValueType>(List<TValueType> Values) {

        //test null/default
        foreach (TValueType v in Values) {
            if (EqualityComparer<TValueType>.Default.Equals(v, default(TValueType))) {
                //value is null or default for its type
            } else {
                //comapre against another value of the same Type
                if (EqualityComparer<TValueType>.Default.Equals(v, SomeOtherValueOfTValueType)) {
                    //value equals
                } else {
                    //value doesn't equal
                }
            }
        }
    }

Meine Frage ist, wie würde ich dieselbe Funktion ausführen, wenn meine Sammlung Werte unterschiedlicher Typen enthielte.

Meine Hauptanliegen sind die erfolgreiche Erkennung von Null- oder Standardwerten und die erfolgreiche Erkennung, ob jeder übergebene Wert mit einem anderen Wert desselben Typs übereinstimmt.

Kann ich dies erreichen, indem ich einfach das Typobjekt übergebe? Ich kann auch nicht wirklich die EqualityComparers verwenden, da ich keine Generika verwenden kann, weil ich in übergeben werde eine unbekannte Anzahl von verschiedenen Typen .

Gibt es eine Lösung?

danke

UPDATE

ok, suchen um, könnte ich den folgenden Code verwenden, um für null/Standard erfolgreich in meinem Szenario zu testen (aus diese SO-Antwort ):

object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;

Ich denke, das könnte funktionieren.

Wie kann ich nun zwei Werte desselben Typs erfolgreich vergleichen, ohne ihre Typen zu kennen? erfolgreich und zuverlässig?

1voto

Adam Robinson Punkte 176996

Die kurze Antwort ist "ja", aber die längere Antwort ist, dass es zwar möglich ist, aber einen nicht unerheblichen Aufwand für Sie und einige Annahmen erfordert, damit es funktioniert. Das eigentliche Problem tritt auf, wenn Sie Werte haben, die als "gleich" gelten würden, wenn sie in stark typisiertem Code verglichen werden, aber keine Referenzgleichheit haben. Die größten Übeltäter werden Wertetypen sein, da ein boxed int mit einem Wert von 1 hat keine referenzielle Gleichheit zu einer anderen Box int mit demselben Wert.

Angesichts dessen müssen Sie den Weg gehen, Dinge wie die IComparable Schnittstelle. Wenn Ihre Typen つねに übereinstimmen, dann ist dies wahrscheinlich ausreichend. Wenn einer Ihrer Werte implementiert IComparable können Sie auf diese Schnittstelle übertragen und mit der anderen Instanz vergleichen, um die Gleichheit festzustellen ( ==0 ). Wenn keiner der beiden dies implementiert, müssen Sie sich wahrscheinlich auf die referenzielle Gleichheit verlassen. Für Referenztypen wird dies funktionieren, es sei denn, es gibt eine benutzerdefinierte Vergleichslogik (eine überladene == Operator auf den Typ, zum Beispiel).

Denken Sie nur daran, dass die Typen GENAU übereinstimmen müssen. Mit anderen Worten, ein int und ein short nicht notwendigerweise so vergleichen, noch würde ein int und eine double .

Sie könnten auch den Weg der Reflexion gehen, um dynamisch die Default Eigenschaft auf den generischen Typ, der zur Laufzeit durch die mitgelieferte Type Variable, aber ich würde das nicht tun wollen, wenn ich es nicht aus Gründen der Leistung und der Kompilierzeitsicherheit (oder deren Fehlen) tun müsste.

1voto

Talljoe Punkte 14181

Handelt es sich bei der Liste der zu prüfenden Typen um eine vorab festgelegte Liste? Wenn ja, können Sie die Funktion Besucher-Muster (und vielleicht sogar wenn nicht, da wir Generika haben). Erstellen Sie eine Methode für Ihre Entitäten (kann mit partiellen Klassen durchgeführt werden), die eine Schnittstelle aufnimmt. Ihre Klasse ruft dann eine Methode auf dieser Schnittstelle auf, die sich selbst übergibt. Die Schnittstellenmethode kann generisch sein, oder Sie können für jeden Typ, den Sie testen wollen, eine Überladung erstellen.

Die Batterie ist fast leer, sonst würde ich ein Beispiel geben.


Fünfzehn Sekunden nach dem Drücken von "Speichern" ging der Rechner in den Ruhezustand über.

Nachdem ich darüber nachgedacht habe, könnte es sein, dass das Besuchermuster Ihr spezielles Problem nicht löst. Ich dachte, Sie versuchten, Entitäten zu vergleichen, aber es scheint, dass Sie Werte testen (also potenziell Ints und Strings).

Aber der Vollständigkeit halber und weil das Besuchermuster ziemlich cool ist, wenn man erst einmal weiß, was es bewirkt, hier eine Erklärung.

Mit dem Visitor-Muster können Sie mehrere Typen handhaben, ohne herausfinden zu müssen, wie man auf den spezifischen Typ castet (Sie entkoppeln den Typ von dem Element, das diesen Typ verwendet). Es funktioniert über zwei Schnittstellen - den Visitor und den Acceptor:

interface IAcceptor
{
  void Accept(IVisitor visitor);
}

interface IVisitor
{
  void Visit(Type1 type1);
  void Visit(Type2 type2);
  .. etc ..
}

Sie können dort optional eine generische Methode verwenden:

interface IVisitor
{
  void Visit<T>(T instance);
}

Die grundlegende Implementierung der Accept-Methode ist:

  void Accept(IVisitor visitor)
  {
    visitor.Visit(this);
  }

Da der Typ, der Accept() implementiert, weiß, um welchen Typ es sich handelt, wird die korrekte Überladung (oder der generische Typ) verwendet. Sie könnten dasselbe mit Reflection und einer Lookup-Tabelle (oder Select-Anweisung) erreichen, aber das ist viel sauberer. Außerdem müssen Sie die Suche nicht in verschiedenen Implementierungen duplizieren - verschiedene Klassen können IVisitor implementieren, um typspezifische Funktionen zu erstellen.

Das Visitor-Muster ist eine Möglichkeit, "Double Dispatch" zu machen. Die Antwort auf diese Frage ist eine weitere Möglichkeit, die Sie vielleicht in etwas umwandeln können, das für Ihren speziellen Fall geeignet ist.

Im Grunde eine langatmige Nicht-Antwort auf Ihr Problem, sorry :) Das Problem fasziniert mich, obwohl - wie wissen Sie, welche Eigenschaft auf die Entität, die Sie gegen testen sollte?

1voto

Alex Yakunin Punkte 6000

Es gibt Object.Equals(object left, object right) statische Methode, sie beruht intern auf Equals(object) Implementierung an einem der angegebenen Argumente verfügbar. Warum vermeiden Sie es zu verwenden?

Die Regeln für die Umsetzung von Gleichstellungsbeauftragten sind in etwa die folgenden:

  1. Erforderlich: Überschreiben Sie Equals(object) y GetHashCode() Methoden
  2. Fakultativ: Umsetzung IEquatable<T> für Ihren Typ (das ist das, was EqualityComparer.Default stützt sich auf)
  3. Optional: Implementierung der Operatoren == und !=

Sie sehen also, wenn Sie sich auf object.Equals(object left, object right) wird dies die beste Lösung sein, wenn man sich auf stark geforderter Teil des Gleichheitsimplementierungsmusters.

Außerdem ist dies die schnellste Option, da sie nur auf virtuelle Methoden zurückgreift. Andernfalls werden Sie auf jeden Fall einige Überlegungen anstellen müssen.

public static void TestGenerics(IList values) {
  foreach (object v in values) {
    if (ReferenceEquals(null,v)) {
      // v is null reference
    }
    else {
      var type = v.GetType();
      if (type.IsValueType && Equals(v, Activator.CreateInstance(type))) {
        // v is default value of its value type
      }
      else {
        // v is non-null value of some reference type
      }
    }
  }
}

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