Entwickler-Ecke

Basistechnologien - Wie prüft man am besten die Klasse eines Objekts?


→Tobi - Sa 08.08.09 09:15
Titel: Wie prüft man am besten die Klasse eines Objekts?
Hallo zusammen,

Ich habe ein Programm, das einen Eingabestring in einen Term umwandelt. Dieser enthält Zahlen, Zeichen usw. , die in einer List<object> gespeichert sind
Nun will ich nur auf die Zahlen eine Operation anwenden. Also muss ich erst prüfen ob es sich um eine Zahl handelt, damit ich mit ihr rechnen kann.

Mir kamen die folgenden drei Möglichkeiten in den Sinn:

1) das Schlüsselwort 'is':

C#-Quelltext
1:
if (array[i] is int) { ... }                    


2) die Methode GetType():

C#-Quelltext
1:
if (array[i].GetType().ToString() == "System.int") { ... }                    


3) eine Basisklasse für alle Elemente mit der Eigenschaft 'typ':

C#-Quelltext
1:
2:
if (array[i].typ == "Zahl") { aktObjekt = (Zahl) array[i]; ... }
// hier muss eine neue Klasse 'Zahl', abgeleitet von der Basisklasse, mit dem Feld 'wert' vorhanden sein



Meine Frage: Wie prüfe ich am besten die Klasse eines Objekts?

Methode 3 möchte ich eigenltich vermeiden, da ich dann nach dem Erkennen des Typs das Objekt erst wieder in ein Objekt vom Typ Zahl konvertieren muss um auf den Wert des Objekts zugreifen zu können.

Methode 2 erscheint mir zwar am einfachsten, aber IntelliSense zeigt bei 'is' an, dass das Programm versucht das Objekt in die jeweilige Klasse zu konvertieren und abhängig davon ob eine Ausnahme erzeugt wird 'true' oder 'false' zurückgibt.

Methode 1 funktioniert eigentlich. Aber wenn Methode 2 genauso schnell funktioniert würde ich diese nehmen. Doch soweit ich weiß sind Ausnahme-Erzeugung und das Abfangen ein relativ langsamer Prozess, oder?


Christian S. - Sa 08.08.09 11:43

Hallo!

Die Mehtoden 1 und 2 sind nicht wirklich identisch (wobei sie in diesem speziellen Anwendungsfall wohl dasselbe Ergebnis liefern dürften), Methode 3 ist nicht nur viel zu aufwändig, sondern auch überflüssig.

Zuerst einmal zum Unterschied der Methoden 1 und 2. Dazu ein kleines Testprogramm:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
namespace ConsoleApplication1
{
    class Mother
    {
        public void DoFoo() { }
    }

    class Child : Mother
    {
        public void DoBar() { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            object blubb = new Child();

            if (blubb is Mother)
                Console.WriteLine("Jupp 1");

            if (blubb.GetType() == typeof(Mother))
                Console.WriteLine("Jupp 2");

            Console.ReadLine();
        }
    }
}


Das Programm wird nur "Jupp 1" ausgeben. Der is-Operator prüft folgendes:
Dokumentation:
An is expression evaluates to true if the provided expression is non-null, and the provided object can be cast to the provided type without causing an exception to be thrown.

The is keyword causes a compile-time warning if the expression is known to always be true or to always be false, but typically evaluates type compatibility at run time.

The is operator cannot be overloaded.

Note that the is operator only considers reference conversions, boxing conversions, and unboxing conversions. Other conversions, such as user-defined conversions, are not considered.

Das heißt, der prüft, ob es möglich ist, ein Objekt in ein anderes zu konvertieren. Im obigen Fall, ob das Objekt eine Instanz der Klasse Mutter oder einer abgeleiteten Klasse ist. Der kursive Teil ist der Unterschied zur Methode 2, denn dort wird ja nur exakt auf diesen einen Typen geprüft, weshalb "Jupp 2" auch nie ausgegeben wird.

Nun zu der Sache mit dem is-Operator und der Exception: Ich glaube, das hast Du falsch verstanden. Der is-Operator gibt zwar zurück, ob es eine Exception geben würde, wenn Du die Konvertierung versuchtest, das ist aber nicht wie der is-Operator das ermittelt. Es gibt ein eigenes IL-Kommando isinst, in welches der is-Operator umgesetzt wird, das sollte also eine ziemlich schnelle Operation sein. Ich würde daher Methode 1 favorisieren.

Solltest Du doch Methode 2 nehmen wollen, solltest Du es vermeiden, den Typen über seine String-Repräsentation zu vergleichen. Das dürfte ziemlich Fehleranfällig und unbequem sein. Der Weg über typeof sollte das besser sein. Zur Not das Ergebnis von typeof cachen, wenn es öfters ausgeführt wird.

Grüße
Christian


&#8594;Tobi - So 09.08.09 18:45

Vielen Dank für die ausführliche und gut verständliche Antwort und das Codebeispiel! Vor allem für den Tipp mit dem typeof() statt der String-Überprüfung und dem Hinweis mit der Arbeitsweise von 'is'. Jetzt kann ich meinen Parser endlich fertigstellen.

MfG Tobi