C # ist die Leistung des Bedieners


102

Ich habe ein Programm, das eine schnelle Leistung erfordert. Innerhalb einer seiner inneren Schleifen muss ich den Typ eines Objekts testen, um festzustellen, ob es von einer bestimmten Schnittstelle erbt.

Eine Möglichkeit hierfür wäre die integrierte Typprüfungsfunktion der CLR. Die eleganteste Methode ist wahrscheinlich das Schlüsselwort 'is':

if (obj is ISpecialType)

Ein anderer Ansatz wäre, der Basisklasse meine eigene virtuelle GetType () -Funktion zu geben, die einen vordefinierten Aufzählungswert zurückgibt (in meinem Fall brauche ich eigentlich nur einen Bool). Diese Methode wäre schnell, aber weniger elegant.

Ich habe gehört, dass es eine IL-Anweisung speziell für das Schlüsselwort 'is' gibt, aber das bedeutet nicht, dass sie schnell ausgeführt wird, wenn sie in eine native Assembly übersetzt wird. Kann jemand einen Einblick in die Leistung von 'is' im Vergleich zur anderen Methode geben?

UPDATE: Danke für all die informierten Antworten! Es scheint, dass einige hilfreiche Punkte in den Antworten verteilt sind: Andrews Punkt über das automatische Durchführen einer Besetzung ist wichtig, aber die von Binary Worrier und Ian gesammelten Leistungsdaten sind auch äußerst nützlich. Es wäre großartig, wenn eine der Antworten so bearbeitet würde, dass sie alle diese Informationen enthält.


2
Übrigens gibt CLR Ihnen keine Möglichkeit, Ihre eigene Type GetType () -Funktion zu erstellen, da sie gegen eine der wichtigsten CLR-Regeln
verstößt

1
Ich bin mir nicht ganz sicher, was Sie unter der Regel "echte Typen" verstehen, aber ich verstehe, dass die CLR über eine integrierte Funktion "Type GetType ()" verfügt. Wenn ich diese Methode verwenden würde, würde sie mit einer Funktion eines anderen Namens eine Aufzählung zurückgeben, sodass es keinen Namens- / Symbolkonflikt geben würde.
JubJub

3
Ich denke, abatishchev bedeutete "Typensicherheit". GetType () ist nicht virtuell, um zu verhindern, dass ein Typ über sich selbst lügt, und damit die Typensicherheit zu gewährleisten.
Andrew Hare

2
Haben Sie darüber nachgedacht, die Typkonformität vorab abzurufen und zwischenzuspeichern, damit Sie dies nicht innerhalb von Schleifen tun müssen? Es scheint, dass jede Perf-Frage immer massiv + 1 ist, aber dies scheint mir nur ein schlechtes Verständnis von c # zu sein. Ist es eigentlich zu langsam? Wie? Was hast du versucht? Offensichtlich nicht viel angesichts Ihrer Kommentare zu den Antworten ...
Gusdor

Antworten:


114

Die Verwendung iskann die Leistung beeinträchtigen, wenn Sie nach Überprüfung des Typs auf diesen Typ umwandeln. isWirkt das Objekt tatsächlich in den Typ, den Sie überprüfen, sodass jede nachfolgende Umwandlung redundant ist.

Wenn Sie trotzdem besetzen wollen, ist hier ein besserer Ansatz:

ISpecialType t = obj as ISpecialType;

if (t != null)
{
    // use t here
}

1
Vielen Dank. Aber wenn ich das Objekt nicht umwandeln möchte, wenn die Bedingung fehlschlägt, wäre es besser, stattdessen eine virtuelle Funktion zu verwenden, um den Typ zu testen?
JubJub

4
@JubJub: nein. Ein Fehler asführt grundsätzlich die gleiche Operation aus wie is(nämlich die Typprüfung). Der einzige Unterschied ist , dass es dann wieder nullstatt false.
Konrad Rudolph

74

Ich bin bei Ian , du willst das wahrscheinlich nicht.

Nur damit Sie wissen, gibt es kaum einen Unterschied zwischen den beiden, über 10.000.000 Iterationen

  • Die Aufzählung erfolgt nach ca. 700 Millisekunden.
  • Der IS-Check erfolgt nach ca. 1000 Millisekunden.

Ich persönlich würde dieses Problem nicht auf diese Weise beheben, aber wenn ich gezwungen wäre, eine Methode auszuwählen, wäre dies die integrierte IS-Prüfung. Der Leistungsunterschied ist es nicht wert, den Codierungsaufwand zu berücksichtigen.

Meine Basis- und abgeleiteten Klassen

class MyBaseClass
{
    public enum ClassTypeEnum { A, B }
    public ClassTypeEnum ClassType { get; protected set; }
}

class MyClassA : MyBaseClass
{
    public MyClassA()
    {
        ClassType = MyBaseClass.ClassTypeEnum.A;
    }
}
class MyClassB : MyBaseClass
{
    public MyClassB()
    {
        ClassType = MyBaseClass.ClassTypeEnum.B;
    }
}

JubJub: Wie gewünscht mehr Infos zu den Tests.

Ich habe beide Tests über eine Konsolen-App (ein Debug-Build) ausgeführt. Jeder Test sieht wie folgt aus

static void IsTest()
{
    DateTime start = DateTime.Now;
    for (int i = 0; i < 10000000; i++)
    {
        MyBaseClass a;
        if (i % 2 == 0)
            a = new MyClassA();
        else
            a = new MyClassB();
        bool b = a is MyClassB;
    }
    DateTime end = DateTime.Now;
    Console.WriteLine("Is test {0} miliseconds", (end - start).TotalMilliseconds);
}

Wenn ich im Release laufe, bekomme ich wie Ian einen Unterschied von 60 - 70 ms.

Weiteres Update - 25. Oktober 2012
Nach ein paar Jahren ist mir etwas daran aufgefallen. Der Compiler kann dies bool b = a is MyClassBin der Version weglassen , da b nirgendwo verwendet wird.

Dieser Code. . .

public static void IsTest()
{
    long total = 0;
    var a = new MyClassA();
    var b = new MyClassB();
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < 10000000; i++)
    {
        MyBaseClass baseRef;
        if (i % 2 == 0)
            baseRef = a;//new MyClassA();
        else
            baseRef = b;// new MyClassB();
        //bool bo = baseRef is MyClassB;
        bool bo = baseRef.ClassType == MyBaseClass.ClassTypeEnum.B;
        if (bo) total += 1;
    }
    sw.Stop();
    Console.WriteLine("Is test {0} miliseconds {1}", sw.ElapsedMilliseconds, total);
}

. . . Zeigt konsistent an, dass der isCheck bei ca. 57 Millisekunden und der Enum-Vergleich bei 29 Millisekunden eingeht.

NB Ich würde den isScheck immer noch vorziehen , der Unterschied ist zu gering, um sich darum zu kümmern


35
+1 für das tatsächliche Testen der Leistung, anstatt anzunehmen.
Jon Tackabury

3
Es ist viel besser, mit der Stoppuhr-Klasse zu testen, als mit DateTime. Jetzt ist das sehr teuer
abatishchev

2
Ich werde das berücksichtigen, aber in diesem Fall glaube ich nicht, dass es das Ergebnis beeinflussen würde. Danke :)
Binary Worrier

11
@Binary Worrier- Ihre neuen Operatorzuordnungen von Klassen werden alle Leistungsunterschiede in den ' is'-Operationen vollständig überschatten. Warum entfernen Sie diese neuen Vorgänge nicht, indem Sie zwei verschiedene vorab zugewiesene Instanzen wiederverwenden, den Code erneut ausführen und Ihre Ergebnisse veröffentlichen?

1
@mcmillab: Ich werde garantieren, dass Sie, was auch immer Sie tun, Engpässe haben, die um viele Größenordnungen größer sind als jede Leistungsminderung, die der isBediener verursacht, und dass das übermäßige Hören von Design und Codierung um den isBediener ein Vermögen kostet Codequalität und wird letztendlich auch in Bezug auf die Leistung selbstzerstörerisch sein. In diesem Fall stehe ich zu meiner Aussage. Der ‚ist‘ Operator wird nie sein werde das Problem mit der Laufzeitleistung.
Binary Worrier

23

Ok, also habe ich mich mit jemandem darüber unterhalten und beschlossen, dies mehr zu testen. Soweit ich das beurteilen kann, sind die Leistung von asund isbeide sehr gut, verglichen mit dem Testen Ihres eigenen Mitglieds oder Ihrer Funktion zum Speichern von Typinformationen.

Ich habe verwendet Stopwatch, was ich gerade gelernt habe, ist möglicherweise nicht der zuverlässigste Ansatz, also habe ich es auch versucht UtcNow. Später habe ich auch den Prozessorzeitansatz ausprobiert, der dem UtcNowEinschließen unvorhersehbarer Erstellungszeiten ähnelt . Ich habe auch versucht, die Basisklasse ohne Virtuals nicht abstrakt zu machen, aber es schien keinen signifikanten Effekt zu haben.

Ich habe dies auf einem Quad Q6600 mit 16 GB RAM ausgeführt. Selbst bei 50-mil-Iterationen schwanken die Zahlen immer noch um +/- 50 Millisekunden, sodass ich nicht zu viel in die kleinen Unterschiede hineinlesen würde.

Es war interessant zu sehen, dass x64 schneller erstellt, aber langsamer ausgeführt wird als x86

x64-Freigabemodus:
Stoppuhr:
As: 561 ms
Is: 597
ms Basiseigenschaft : 539 ms Basisfeld : 555
ms
Basis-RO-Feld: 552 ms
Virtual GetEnumType () -Test: 556 ms
Virtual IsB () -Test: 588 ms
Erstellungszeit: 10416 ms

UtcNow:
As: 499 ms
Is: 532 ms
Base-Eigenschaft: 479 ms
Base-Feld: 502 ms
Base-RO-Feld: 491 ms
Virtual GetEnumType (): 502 ms
Virtual bool IsB (): 522 ms
Erstellungszeit: 285 ms (Diese Zahl scheint mit UtcNow unzuverlässig zu sein. Ich erhalte auch 109 ms und 806 ms.)

x86-Freigabemodus:
Stoppuhr:
As: 391 ms
Is: 423
ms Basiseigenschaft : 369 ms Basisfeld : 321
ms
Basis-RO-Feld: 339 ms
Virtual GetEnumType () -Test: 361 ms
Virtual IsB () -Test: 365 ms
Erstellungszeit: 14106 ms

UtcNow:
As: 348ms
Is: 375ms
Base-Eigenschaft: 329ms
Base-Feld: 286 ms
Base-RO-Feld: 309 ms
Virtual GetEnumType (): 321 ms
Virtual bool IsB (): 332 ms
Erstellungszeit: 544 ms (Diese Zahl scheint mit UtcNow unzuverlässig zu sein.)

Hier ist der größte Teil des Codes:

    static readonly int iterations = 50000000;
    void IsTest()
    {
        Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;
        MyBaseClass[] bases = new MyBaseClass[iterations];
        bool[] results1 = new bool[iterations];

        Stopwatch createTime = new Stopwatch();
        createTime.Start();
        DateTime createStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            if (i % 2 == 0) bases[i] = new MyClassA();
            else bases[i] = new MyClassB();
        }
        DateTime createStop = DateTime.UtcNow;
        createTime.Stop();


        Stopwatch isTimer = new Stopwatch();
        isTimer.Start();
        DateTime isStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] =  bases[i] is MyClassB;
        }
        DateTime isStop = DateTime.UtcNow; 
        isTimer.Stop();
        CheckResults(ref  results1);

        Stopwatch asTimer = new Stopwatch();
        asTimer.Start();
        DateTime asStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i] as MyClassB != null;
        }
        DateTime asStop = DateTime.UtcNow; 
        asTimer.Stop();
        CheckResults(ref  results1);

        Stopwatch baseMemberTime = new Stopwatch();
        baseMemberTime.Start();
        DateTime baseStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassType == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseStop = DateTime.UtcNow;
        baseMemberTime.Stop();
        CheckResults(ref  results1);

        Stopwatch baseFieldTime = new Stopwatch();
        baseFieldTime.Start();
        DateTime baseFieldStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseFieldStop = DateTime.UtcNow;
        baseFieldTime.Stop();
        CheckResults(ref  results1);


        Stopwatch baseROFieldTime = new Stopwatch();
        baseROFieldTime.Start();
        DateTime baseROFieldStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseROFieldStop = DateTime.UtcNow;
        baseROFieldTime.Stop();
        CheckResults(ref  results1);

        Stopwatch virtMethTime = new Stopwatch();
        virtMethTime.Start();
        DateTime virtStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].GetClassType() == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime virtStop = DateTime.UtcNow;
        virtMethTime.Stop();
        CheckResults(ref  results1);

        Stopwatch virtMethBoolTime = new Stopwatch();
        virtMethBoolTime.Start();
        DateTime virtBoolStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].IsB();
        }
        DateTime virtBoolStop = DateTime.UtcNow;
        virtMethBoolTime.Stop();
        CheckResults(ref  results1);


        asdf.Text +=
        "Stopwatch: " + Environment.NewLine 
          +   "As:  " + asTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
           +"Is:  " + isTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
           + "Base property:  " + baseMemberTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base field:  " + baseFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base RO field:  " + baseROFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType() test:  " + virtMethTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual IsB() test:  " + virtMethBoolTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Create Time :  " + createTime.ElapsedMilliseconds + "ms" + Environment.NewLine + Environment.NewLine+"UtcNow: " + Environment.NewLine + "As:  " + (asStop - asStart).Milliseconds + "ms" + Environment.NewLine + "Is:  " + (isStop - isStart).Milliseconds + "ms" + Environment.NewLine + "Base property:  " + (baseStop - baseStart).Milliseconds + "ms" + Environment.NewLine + "Base field:  " + (baseFieldStop - baseFieldStart).Milliseconds + "ms" + Environment.NewLine + "Base RO field:  " + (baseROFieldStop - baseROFieldStart).Milliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType():  " + (virtStop - virtStart).Milliseconds + "ms" + Environment.NewLine + "Virtual bool IsB():  " + (virtBoolStop - virtBoolStart).Milliseconds + "ms" + Environment.NewLine + "Create Time :  " + (createStop-createStart).Milliseconds + "ms" + Environment.NewLine;
    }
}

abstract class MyBaseClass
{
    public enum ClassTypeEnum { A, B }
    public ClassTypeEnum ClassType { get; protected set; }
    public ClassTypeEnum ClassTypeField;
    public readonly ClassTypeEnum ClassTypeReadonlyField;
    public abstract ClassTypeEnum GetClassType();
    public abstract bool IsB();
    protected MyBaseClass(ClassTypeEnum kind)
    {
        ClassTypeReadonlyField = kind;
    }
}

class MyClassA : MyBaseClass
{
    public override bool IsB() { return false; }
    public override ClassTypeEnum GetClassType() { return ClassTypeEnum.A; }
    public MyClassA() : base(MyBaseClass.ClassTypeEnum.A)
    {
        ClassType = MyBaseClass.ClassTypeEnum.A;
        ClassTypeField = MyBaseClass.ClassTypeEnum.A;            
    }
}
class MyClassB : MyBaseClass
{
    public override bool IsB() { return true; }
    public override ClassTypeEnum GetClassType() { return ClassTypeEnum.B; }
    public MyClassB() : base(MyBaseClass.ClassTypeEnum.B)
    {
        ClassType = MyBaseClass.ClassTypeEnum.B;
        ClassTypeField = MyBaseClass.ClassTypeEnum.B;
    }
}

45
(Einige Bonus 5-inspirierte Shakespeare ...) Sein oder Nichtsein: das ist die Frage: Ob es im Code edler ist, unter den Aufzählungen und Eigenschaften abstrakter Grundlagen zu leiden, oder die Angebote eines Vermittlers anzunehmen Linguist Und indem Sie sich auf seine Anweisung berufen, vertrauen Sie ihnen? Zu raten: sich zu wundern; Nicht mehr; und durch einen Zeitpunkt, um zu erkennen, beenden wir die Kopfschmerzen und die tausend unbewussten Wunder, denen zeitgebundene Programmierer erben. Es ist eine Schließung, die man sich nur wünschen kann. Sterben, nein, aber schlafen; Ja, ich werde schlafen, vielleicht träumt man davon und wie in dem, was von der Basis der Klasse abgeleitet werden kann.
Jared Thirsk

Können wir daraus schließen, dass der Zugriff auf eine Eigenschaft unter x64 schneller ist als der Zugriff auf ein Feld !!! Weil das eine verdammt große Überraschung für mich ist, wie das sein kann?
Didier A.

1
Daraus würde ich nicht schließen, weil: "Selbst bei 50-mil-Iterationen springen die Zahlen immer noch um +/- 50 Millisekunden, damit ich nicht zu viel in die kleinen Unterschiede hineinlese."
Jared Thirsk

16

Andrew ist richtig. Bei der Codeanalyse wird dies von Visual Studio als unnötige Besetzung gemeldet.

Eine Idee (ohne zu wissen, was Sie tun, ist ein Schuss in die Dunkelheit), aber mir wurde immer geraten, solche Überprüfungen zu vermeiden und stattdessen eine andere Klasse zu haben. Lassen Sie die Klasse also wissen, wie sie sich selbst verarbeitet, anstatt einige Überprüfungen durchzuführen und je nach Typ unterschiedliche Aktionen auszuführen ...

zB Obj kann ISpecialType oder IType sein;

Für beide ist eine DoStuff () -Methode definiert. Für IType kann es einfach zurückgeben oder benutzerdefinierte Aufgaben ausführen, während ISpecialType andere Aufgaben ausführen kann.

Dadurch wird jegliches Casting vollständig entfernt, der Code wird sauberer und einfacher zu warten, und die Klasse weiß, wie sie ihre eigenen Aufgaben erledigt.


Ja, da alles, was ich tun werde, wenn der Typ test true ist, darin besteht, eine bestimmte Schnittstellenmethode darauf aufzurufen, könnte ich diese Schnittstellenmethode einfach in die Basisklasse verschieben und sie standardmäßig nichts tun lassen. Das ist möglicherweise eleganter als das Erstellen einer virtuellen Funktion zum Testen des Typs.
JubJub

Ich habe nach Abatishchevs Kommentaren einen ähnlichen Test wie Binary Worrier durchgeführt und nur einen Unterschied von 60 ms über 10.000.000 Iterationen festgestellt.
Ian

1
Wow, danke für die Hilfe. Ich nehme an, ich werde mich vorerst an die Typprüfungsoperatoren halten, es sei denn, es erscheint angemessen, die Klassenstruktur neu zu organisieren. Ich werde den 'as'-Operator verwenden, wie Andrew vorgeschlagen hat, da ich nicht redundant wirken möchte.
JubJub

15

Ich habe einen Leistungsvergleich über zwei Möglichkeiten des Typvergleichs durchgeführt

  1. myobject.GetType () == typeof (MyClass)
  2. Mein Objekt ist MyClass

Das Ergebnis ist: Die Verwendung von "is" ist ungefähr 10x schneller !!!

Ausgabe:

Zeit für den Typvergleich: 00: 00: 00.456

Zeit für den Is-Vergleich: 00: 00: 00.042

Mein Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication3
{
    class MyClass
    {
        double foo = 1.23;
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass myobj = new MyClass();
            int n = 10000000;

            Stopwatch sw = Stopwatch.StartNew();

            for (int i = 0; i < n; i++)
            {
                bool b = myobj.GetType() == typeof(MyClass);
            }

            sw.Stop();
            Console.WriteLine("Time for Type-Comparison: " + GetElapsedString(sw));

            sw = Stopwatch.StartNew();

            for (int i = 0; i < n; i++)
            {
                bool b = myobj is MyClass;
            }

            sw.Stop();
            Console.WriteLine("Time for Is-Comparison: " + GetElapsedString(sw));
        }

        public static string GetElapsedString(Stopwatch sw)
        {
            TimeSpan ts = sw.Elapsed;
            return String.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
        }
    }
}

13

Punkt Andrew Hare machte über Leistungsverluste, wenn Sie eine isPrüfung durchführen und dann die Besetzung gültig war, aber in C # 7.0 können wir die Übereinstimmung der Hexenmuster überprüfen, um später eine zusätzliche Besetzung zu vermeiden:

if (obj is ISpecialType st)
{
   //st is in scope here and can be used
}

Wenn Sie zwischen mehreren Typen prüfen müssen, können Sie mit C # 7.0-Mustervergleichskonstrukten jetzt folgende switchTypen ausführen:

public static double ComputeAreaModernSwitch(object shape)
{
    switch (shape)
    {
        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Rectangle r:
            return r.Height * r.Length;
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
    }
}

Weitere Informationen zum Mustervergleich in C # finden Sie in der Dokumentation hier .


1
Sicher eine gültige Lösung, aber diese C # -Muster-Matching-Funktion macht mich traurig, wenn sie solchen Code wie "Feature-Neid" fördert. Sicherlich sollten wir uns um die Kapselung von Logik bemühen, bei der nur die abgeleiteten Objekte "wissen", wie sie ihre eigene Fläche berechnen, und dann nur den Wert zurückgeben?
Dib

2
SO benötigt Filterschaltflächen (für die Frage) für Antworten, die für neuere Versionen eines Frameworks, einer Plattform usw. gelten. Diese Antwort bildet die Grundlage für die richtige für C # 7.
Nick Westgate

1
@ Dib OOP-Ideale werden aus dem Fenster geworfen, wenn Sie mit Typen / Klassen / Schnittstellen arbeiten, die Sie nicht kontrollieren. Dieser Ansatz ist auch nützlich, wenn Sie das Ergebnis einer Funktion verarbeiten, die einen von vielen Werten völlig unterschiedlicher Typen zurückgeben kann (da C # noch keine Union-Typen unterstützt - Sie können Bibliotheken wie verwenden, OneOf<T...>aber sie weisen erhebliche Mängel auf). .
Dai

4

Für den Fall, dass sich jemand wundert, habe ich Tests in Unity Engine 2017.1 mit der Skript-Laufzeitversion .NET4.6 (Experimantal) auf einem Notebook mit i5-4200U-CPU durchgeführt. Ergebnisse:

Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35

Vollständiger Artikel: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html


Artikel Link ist tot.
James Wilkins

@ James Link wiederbelebt.
Gru

Gutes Zeug - aber ich habe dich nicht herabgestimmt (eigentlich habe ich sowieso hochgestimmt); Falls Sie sich fragen. :)
James Wilkins

-3

Mir wurde immer geraten, solche Überprüfungen zu vermeiden und stattdessen eine andere Klasse zu haben. Lassen Sie die Klasse also wissen, wie sie sich selbst verarbeitet, anstatt einige Überprüfungen durchzuführen und je nach Typ unterschiedliche Aktionen auszuführen ...

zB Obj kann ISpecialType oder IType sein;

Für beide ist eine DoStuff () -Methode definiert. Für IType kann es einfach zurückgeben oder benutzerdefinierte Aufgaben ausführen, während ISpecialType andere Aufgaben ausführen kann.

Dadurch wird jegliches Casting vollständig entfernt, der Code wird sauberer und einfacher zu warten, und die Klasse weiß, wie sie ihre eigenen Aufgaben erledigt.


1
Dies beantwortet die Frage nicht. Auf jeden Fall wissen Klassen aufgrund mangelnden Kontexts möglicherweise nicht immer, wie sie sich selbst verarbeiten sollen. Wir wenden eine ähnliche Logik auf die Ausnahmebehandlung an, wenn wir zulassen, dass Ausnahmen die Aufrufkette durchlaufen, bis eine Methode / Funktion über genügend Kontext verfügt, um die Fehler zu behandeln.
Vakhtang
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.