7 Stimmen

C#: Force Compiler-Fehler bei Verwendung von myObj.ToString()

Ich habe eine Klasse, die eine Reihe von Eigenschaften enthält. Es ist ein Fehler eines Programmierers, wenn er ToString() für ein Objekt dieses Typs aufruft. Nehmen Sie diesen Beispielcode:

using System;

public class Foo
{
    public int ID = 123;
    public string Name = "SomeName";

    private string ToString() { return null; }
}

public class MyClass
{
    public static void Main()
    {
        Foo myObj = new Foo();
        WL("I want this to be a compiler error: {0}", myObj.ToString());
        RL();
    }

    #region Helper methods

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    #endregion
}

Man könnte argumentieren, dass, wenn ID ist, was die meisten Menschen wollen als eine Zeichenfolge geschrieben, dann sollte ich implementieren ToString so, dass es die ID zurückgibt. Ich halte dies jedoch für eine schlechte Praxis, da Programmierer "versehentlich" funktionierenden Code erhalten. Ein Programmierer, der meine Klasse verwendet, sollte angeben, was er will.

Stattdessen möchte ich, dass, wenn jemand myObj.ToString() aufruft, dies als Kompilierzeitfehler angezeigt wird. Ich dachte, ich könnte das tun, indem ich eine private ToString()-Funktion erstelle, aber das funktioniert nicht.

Der Grund, warum ich das angesprochen habe, ist, dass wir am Ende eine Abfragezeichenfolge hatten, die den voll qualifizierten Klassennamen und nicht eine ID enthielt.

Die Frage ist also: Gibt es eine Möglichkeit, die Funktion ToString() zu "verstecken", so dass der Aufruf auf ein Objekt meiner Klasse einen Compilerfehler verursacht?

45voto

Jason Jackson Punkte 16792

Ich kann nicht genug betonen, wie schlecht diese Idee ist.

ToString() ist Teil des Objektvertrags in .Net. Wenn Sie es nicht implementieren wollen, dann überschreiben Sie es nicht, und lassen Sie es einfach die Typinformationen zurückgeben. Welchen Schaden könnte das verursachen?

Ich will ja nicht so negativ sein, aber ich bin absolut fassungslos, dass jemand die ToString() .

Einige zusätzliche Punkte:

  1. Warum gehen die Programmierer, die diese Klasse verwenden, davon aus, dass ToString() eine ID zurückgeben wird? Tun dies auch andere Klassen in Ihrem Ökosystem? Man kann argumentieren, dass ToString() sollte einige aussagekräftige Daten liefern. Aber Sie sollten wirklich nicht gegen die Ergebnisse einer ToString() anrufen. ToString() ist für eine String-Darstellung der Klasse, Punkt. Das klingt nach einem Ausbildungs- oder Kommunikationsproblem zwischen Programmierern oder Abteilungen.

  2. Verkrüppelung ToString() in irgendeiner Weise, ob Sie herausfinden können, wie zur Kompilierungszeit oder durch das Auslösen einer Ausnahme zur Laufzeit, wird Wellen schlagen. Ich habe niemals Ich habe das noch nie gesehen und würde nicht erwarten, dass eine Klasse, die ich verwende, dieses Verhalten an den Tag legt. Ich denke, dass die meisten Programmierer die gleichen Erwartungen haben würden. Werden zukünftige Programmierer, die Ihre Klasse verwenden, dies erwarten? Welche Bugs und Wartungsalpträume verursachen Sie damit?

  3. Welche Auswirkung hat dies auf die IDE oder den Debugger, die sich auf ToString() ?

  4. Welche Auswirkungen hat dies bei der Verwendung von Datenbindungstechnologien, die nicht an einen bestimmten Typ binden, sondern zur Laufzeit Reflection verwenden, um Werte herauszuziehen? Die meisten Datenbindungen werden auf den Aufruf von ToString() auf ein Objekt, wenn kein Mitglied zur Verwendung angegeben ist.

0 Stimmen

Ich stimme zu, dass diese Idee eine schlechte Praxis ist.

0 Stimmen

Ich stimme zu, aber sein eigentliches Problem ist, dass die Benutzer seiner Klasse fälschlicherweise annehmen, dass ToString() die ID des Objekts liefert. Was wäre die beste Lösung für dieses Problem?

0 Stimmen

Ich würde fragen, warum sie annehmen, dass ToString() eine ID liefert? Gibt es andere Klassen mit ToString(), die eine ID zurückgeben, im Ökosystem? Ist dies ein Ausbildungs-/Kommunikationsproblem (nicht alles sollte im Code festgelegt werden)?

20voto

Sander Punkte 24565

Mit dem Attribut Obsolete können Sie dies tun.

[Obsolete("Use the XYZ properties instead of .ToString() on Foobar", true)]

Der boolesche Wert am Ende gibt an, ob der Compiler die Verwendung dieses Elements als Fehler betrachten soll.

0 Stimmen

Ehh... dies ist wahrscheinlich weniger "hacky" als Überschreiben der ToString() mit einer falschen Rückkehr.

1 Stimmen

Wenigstens funktioniert es. Vielleicht müssen die .NET-Leute ein Attribut "IToldYouNeverToCallMe" hinzufügen.

0 Stimmen

Das funktioniert, und das ist die Lösung, für die ich mich entscheide, es sei denn, Jason Jackson liefert einen guten Grund, warum ich es nicht tun sollte.

7voto

mockobject Punkte 1727

Ich bin mit der Verwendung der Eigenschaft Obsolete aus mehreren Gründen überhaupt nicht einverstanden.

Zunächst erhalten Sie nun eine Warnung für die Methode ToString(), die Sie überschrieben und mit der Eigenschaft Obsolete gekennzeichnet haben:

    [Obsolete("dont' use", true)]
    public override string ToString()
    {
        throw new Exception("don't use");
    }

führt zu dieser Warnung: Warning 1 Obsolete member 'ClassLibrary1.Foo.ToString()' overrides non-obsolete member 'object.ToString()' d: \source\ClassLibrary1\ClassLibrary1\Class1.cs 11 32 Klassenbibliothek1

Daher haben Sie jetzt eine ständige Warnung in Ihrem Code. Darüber hinaus wird Ihr Problem dadurch nicht wirklich gelöst. Was passiert, wenn etwas im Framework jetzt implizit ToString() aufruft? Das Ergebnis des folgenden Codes ist, dass der Code im Körper von ToString() weiterhin aufgerufen wird:

        Foo myObj = new Foo();

        Console.WriteLine(myObj);

Jetzt haben Sie also eine Warnung in Ihrem Code, die einen Entwickler nicht wirklich davon abhält, das Gleiche noch einmal zu tun. Ich denke, der richtige Schritt ist hier, einen Weg zu finden, um eine entsprechende Ausnahme zur Laufzeit auszulösen, anstatt zu versuchen, mit den .net-Objektverträgen zu spielen.

Vorschlag zum Abfangen des Problems zur Kompilierzeit: Mir ist aufgefallen, dass ich bisher keinen Lösungsvorschlag für dieses Problem gemacht habe. Ich weiß nicht wirklich, welches Format Ihre ID ist in sicher, so dass ich nur eine Vermutung, dass es ein int, aber warum nicht schützen, was auch immer ist die Erstellung der URL mit dem Querystring und übergeben Sie die ID als ein int. Auf diese Weise kann ein Entwickler nicht versehentlich eine bedeutungslose Zeichenkette einfügen, ohne dass ein Kompilierungsfehler auftritt. Wie dies zum Beispiel:

public string CreateItemUrl(int itemId)
{
   return string.Format("someurl.aspx?id={0}", itemId);
}

Nun, ich rufe dies an:

CreateItemUrl(myObj.Id);

wird viel stärker typisiert und weniger fehleranfällig als:

string theUrl = string.Format("someurl.aspx?id={0}", myObj);

0 Stimmen

Die Warnung kann mit einem Pragma unterdrückt werden. Das mit dem impliziten Aufruf von ToString ist ein gutes Argument, das habe ich nicht bedacht. Ich finde es enttäuschend, wenn ich nicht kontrollieren kann, welche Methoden meine Klasse enthält und ich meinen Code nicht statisch (zur Kompilierzeit) überprüfen kann.

7voto

MichaelGG Punkte 9900

Ich würde einen gemischten Ansatz wählen. (Hey, geht es bei SO nicht darum, andere Antworten zu kombinieren? :))

Erstellen Sie zunächst einen neuen ToString, der void zurückgibt. Kein Rückgabewert bedeutet, dass sie ihn nicht verwenden können, um versehentlich schönen Code zu erhalten:

public new void ToString() { }

Als Nächstes fügen Sie das Obsolete-Attribut hinzu, damit die Benutzer beim Aufruf eine Warnung erhalten, die ihnen mitteilt, dass ToString schlecht ist.

Auf diese Weise brauchen Sie ToString nicht zu überschreiben, sondern verstecken es einfach mit etwas, das nutzlos ist. Die Tatsache, dass es keine Rückgabe hat, wird den gesamten Code brechen und somit einen Compiler-Fehler zusätzlich zu der veralteten Nachricht verursachen.

Leute, die zu Object casten, sind nicht Ihr Anliegen, wenn ich Ihre Frage richtig verstehe. Sie wollen nicht verhindern, dass Leute ToString aufrufen und Typinformationen erhalten, Sie wollen verhindern, dass sie versehentlich denken, dass ToString ein nützliches Ergebnis liefert.

Bearbeiten: Bitte werfen Sie keine Ausnahme oder überschreiben Sie ToString. Das würde "schlechte Dinge" verursachen, wenn Ihr Objekt als Objekt behandelt wird. Verwenden Sie einfach "new" ermöglicht die Vorteile, die Sie gefragt haben, ohne andere Frameworks zu vermasseln.

0 Stimmen

Vielen Dank, in meinem Fall übersteuert der vorhandene Code die Methode equals. Ich musste sie mit einem anderen Parameter überladen. Ich möchte andere dazu zwingen, meine neue überladene Methode anstelle der Basismethode equal zu verwenden. Ihr Vorschlag hat mir sehr geholfen, vielen Dank

0 Stimmen

+1, da dies das Problem ohne die Überschreibung löst. In meinem Fall musste ich verhindern, dass ich versehentlich mit ToString um den String-Wert eines Objekts zu erhalten, das sowohl eine valueString y valueInt Eigenschaft. Ich nehme an, ToString könnte einfach valueString zurückgeben, aber ich würde lieber die explizite Verwendung haben.

1voto

EBGreen Punkte 35149

Verwenden Sie das Schlüsselwort override mit einer öffentlichen Funktion ToString(), um die Methode System.Object ToString() außer Kraft zu setzen.

1 Stimmen

Ich habe auch darüber nachgedacht, aber festgestellt, dass es eine schlechte Idee ist, weil die IDE die Methode .ToString() an vielen Stellen verwendet.

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