Beim Programmieren von Schnittstellen habe ich festgestellt, dass ich viel Casting oder Objekttypkonvertierung mache.
Gibt es einen Unterschied zwischen diesen beiden Konvertierungsmethoden? Wenn ja, gibt es einen Kostenunterschied oder wie wirkt sich dies auf mein Programm aus?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
Was ist "im Allgemeinen" die bevorzugte Methode?
Antworten:
Die Antwort unter der Zeile wurde 2008 geschrieben.
C # 7 führte den Mustervergleich ein, der den
as
Operator weitgehend ersetzt hat , wie Sie jetzt schreiben können:Beachten Sie, dass dies danach
tt
noch im Geltungsbereich liegt, aber nicht definitiv zugewiesen ist. (Es ist definitiv imif
Körper zugeordnet.) Das ist in einigen Fällen etwas ärgerlich. Wenn Sie also wirklich daran interessiert sind, die kleinstmögliche Anzahl von Variablen in jedem Bereich einzuführen, möchten Sie möglicherweise immer nochis
eine Besetzung verwenden, gefolgt von einer Besetzung.Ich glaube, keine der bisherigen Antworten (zum Zeitpunkt des Beginns dieser Antwort!) Hat wirklich erklärt, wo es sich lohnt, welche zu verwenden.
Tu das nicht:
Dies wird nicht nur zweimal überprüft, sondern es können auch verschiedene Dinge überprüft werden, wenn
randomObject
es sich eher um ein Feld als um eine lokale Variable handelt. Es ist möglich, dass das "Wenn" bestanden wird, aber dann die Umwandlung fehlschlägt, wenn ein anderer Thread den WertrandomObject
zwischen den beiden ändert .Wenn
randomObject
wirklich soll eine Instanz seinTargetType
, das heißt , wenn es nicht ist , das bedeutet ein Fehler da ist, dann ist Gießen die richtige Lösung. Das löst sofort eine Ausnahme aus, was bedeutet, dass unter falschen Annahmen keine Arbeit mehr ausgeführt wird und die Ausnahme die Art des Fehlers korrekt anzeigt.Wenn es sich
randomObject
möglicherweise um eine Instanz vonTargetType
undTargetType
um einen Referenztyp handelt, verwenden Sie folgenden Code:Wenn es sich
randomObject
möglicherweise um eine Instanz vonTargetType
undTargetType
um einen Werttyp handelt, können wir ihn nichtas
mit sichTargetType
selbst verwenden, aber wir können einen nullbaren Typ verwenden:(Hinweis: Derzeit ist dies tatsächlich langsamer als + Besetzung . Ich denke, es ist eleganter und konsistenter, aber los geht's.)
Wenn Sie den konvertierten Wert wirklich nicht benötigen, aber nur wissen müssen, ob es sich um eine Instanz von TargetType handelt, ist der
is
Operator Ihr Freund. In diesem Fall spielt es keine Rolle, ob TargetType ein Referenztyp oder ein Werttyp ist.Es kann andere Fälle mit Generika geben, in denen dies
is
nützlich ist (weil Sie möglicherweise nicht wissen, ob T ein Referenztyp ist oder nicht, Sie können es also nicht als verwenden), aber sie sind relativ dunkel.Ich habe mit ziemlicher Sicherheit schon früher
is
für den Werttyp-Fall verwendet, ohne daran gedacht zu haben, einen nullbaren Typ zu verwenden undas
zusammen :)BEARBEITEN: Beachten Sie, dass keiner der oben genannten Punkte die Leistung betrifft, außer dem Fall des Werttyps, bei dem ich festgestellt habe, dass das Entpacken in einen nullbaren Werttyp tatsächlich langsamer ist - aber konsistent.
Gemäß der Antwort von naasking sind is-and-cast oder is-and-as mit modernen JITs so schnell wie eine Nullprüfung, wie der folgende Code zeigt:
Auf meinem Laptop werden diese alle in ca. 60 ms ausgeführt. Zwei Dinge zu beachten:
Machen wir uns also keine Sorgen um die Leistung. Sorgen wir uns um Korrektheit und Konsistenz.
Ich behaupte, dass is-and-cast (oder is-and-as) beim Umgang mit Variablen beide unsicher sind, da sich der Typ des Wertes, auf den er sich bezieht, aufgrund eines anderen Threads zwischen dem Test und dem Cast ändern kann. Das wäre eine ziemlich seltene Situation - aber ich hätte lieber eine Konvention, die ich konsequent anwenden kann.
Ich behaupte auch, dass der As-Then-Null-Check eine bessere Trennung der Bedenken ermöglicht. Wir haben eine Anweisung, die eine Konvertierung versucht, und dann eine Anweisung, die das Ergebnis verwendet. Das is-and-cast oder is-and-as führt einen Test und dann einen weiteren Versuch durch, den Wert zu konvertieren.
Anders ausgedrückt, würde jemand jemals schreiben:
Das ist genau das, was Is-and-Cast tut - wenn auch offensichtlich billiger.
quelle
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
oder verwendenswitch
/case
sehen docs"as" gibt NULL zurück, wenn keine Umwandlung möglich ist.
Das vorherige Casting löst eine Ausnahme aus.
Für die Leistung ist das Auslösen einer Ausnahme normalerweise zeitaufwändiger.
quelle
Hier ist eine andere Antwort mit einem IL-Vergleich. Betrachten Sie die Klasse:
Schauen Sie sich nun die IL an, die jede Methode erzeugt. Selbst wenn die Op-Codes für Sie nichts bedeuten, können Sie einen großen Unterschied feststellen: isinst wird aufgerufen, gefolgt von castclass in der DirectCast-Methode. Also grundsätzlich zwei Anrufe statt eines.
Das Schlüsselwort isinst im Vergleich zur castclass
Dieser Blog-Beitrag hat einen anständigen Vergleich zwischen den beiden Möglichkeiten. Seine Zusammenfassung lautet:
Ich persönlich benutze immer As, weil es leicht zu lesen ist und vom .NET-Entwicklungsteam (oder Jeffrey Richter sowieso) empfohlen wird.
quelle
Einer der subtileren Unterschiede zwischen den beiden besteht darin, dass das Schlüsselwort "as" nicht für das Casting verwendet werden kann, wenn ein Cast-Operator beteiligt ist:
Dies wird in der letzten Zeile nicht kompiliert (obwohl ich denke, dass dies in früheren Versionen der Fall war), da die Schlüsselwörter "as" Cast-Operatoren nicht berücksichtigen. Die Linie
string cast = (string)f;
funktioniert aber gut.quelle
as löst niemals eine Ausnahme aus, wenn die Konvertierung nicht durchgeführt werden kann und stattdessen null zurückgegeben wird ( wie nur bei Referenztypen). Die Verwendung von as entspricht also im Grunde genommen
C-artige Casts hingegen lösen eine Ausnahme aus, wenn keine Konvertierung möglich ist.
quelle
Nicht wirklich eine Antwort auf Ihre Frage, aber was ich denke, ist ein wichtiger verwandter Punkt.
Wenn Sie auf eine Schnittstelle programmieren, sollten Sie keine Besetzung benötigen. Hoffentlich sind diese Darsteller sehr selten. Wenn nicht, müssen Sie wahrscheinlich einige Ihrer Schnittstellen überdenken.
quelle
Bitte ignorieren Sie den Rat von Jon Skeet, bezüglich: Vermeiden Sie Test-and-Cast-Muster, dh:
Die Idee, dass dies mehr kostet als eine Besetzung und ein Null-Test, ist ein Mythos :
Es ist eine Mikrooptimierung, die nicht funktioniert. Ich habe einige echte Tests durchgeführt , und Test-and-Cast ist tatsächlich schneller als Cast-and-Null-Vergleich, und es ist auch sicherer, weil Sie nicht die Möglichkeit haben, eine Nullreferenz im Bereich außerhalb des Falls zu haben Scheitern.
Wenn Sie einen Grund suchen, warum Test-and-Cast schneller oder zumindest nicht langsamer ist, gibt es einen einfachen und komplexen Grund.
Einfach: Selbst naive Compiler werden zwei ähnliche Operationen wie Test-and-Cast zu einem einzigen Test und Zweig zusammenführen. Cast-and-Null-Test kann zwei Tests und einen Zweig erzwingen, einen für den Typentest und die Konvertierung in Null bei einem Fehler, einen für die Nullprüfung selbst. Zumindest werden beide auf einen einzigen Test und Zweig optimiert, sodass Test and Cast weder langsamer noch schneller als Cast and Null-Test ist.
Komplex: Warum Test und Cast schneller sind: Cast-and-Null-Test führt eine weitere Variable in den äußeren Bereich ein, die der Compiler auf Lebendigkeit hin verfolgen muss, und kann diese Variable möglicherweise nicht optimieren, je nachdem, wie komplex Ihr Steuerelement ist. Fluss ist. Umgekehrt führt test-and-cast eine neue Variable nur in einem begrenzten Bereich ein, sodass der Compiler weiß, dass die Variable nach dem Beenden des Bereichs tot ist, und so die Registerzuordnung besser optimieren kann.
Bitte lassen Sie diesen "Cast-and-Null-Test ist besser als Test-and-Cast" -Ratschlag DIE. BITTE. Test-and-Cast ist sicherer und schneller.
quelle
ref
Parameter handelt. Es ist sicher für lokale Variablen, aber nicht für Felder. Ich wäre daran interessiert, Ihre Benchmarks auszuführen, aber der Code, den Sie in Ihrem Blog-Beitrag angegeben haben, ist nicht vollständig. Ich bin damit einverstanden, keine Mikrooptimierung vorzunehmen, aber ich denke nicht, dass die zweimalige Verwendung des Werts lesbarer oder eleganter ist als die Verwendung von "as" und eines Nullitätstests. (Ich würde definitiv eine gerade Besetzung anstelle von "wie" nach einem is verwenden, übrigens.)Wenn die Umwandlung fehlschlägt, löst das Schlüsselwort 'as' keine Ausnahme aus. Stattdessen wird die Variable auf null (oder auf ihren Standardwert für Werttypen) gesetzt.
quelle
Dies ist keine Antwort auf die Frage, sondern ein Kommentar zum Codebeispiel der Frage:
Normalerweise sollten Sie kein Objekt von z. B. IMyInterface in MyClass umwandeln müssen. Das Tolle an Schnittstellen ist, dass Sie sich nicht darum kümmern müssen, welche Art von Objekt Sie erhalten, wenn Sie ein Objekt als Eingabe verwenden, die eine Schnittstelle implementiert.
Wenn Sie IMyInterface in MyClass umwandeln, gehen Sie bereits davon aus, dass Sie ein Objekt vom Typ MyClass erhalten, und es macht keinen Sinn, IMyInterface zu verwenden. Wenn Sie Ihren Code mit anderen Klassen füttern, die IMyInterface implementieren, wird Ihr Code beschädigt ...
Nun mein Rat: Wenn Ihre Schnittstellen gut gestaltet sind, können Sie viel Typografie vermeiden.
quelle
Der
as
Operator kann nur für Referenztypen verwendet werden, er kann nicht überladen werden und er wird zurückgegeben,null
wenn der Vorgang fehlschlägt. Es wird niemals eine Ausnahme auslösen.Casting kann für alle kompatiblen Typen verwendet werden, es kann überladen werden und es wird eine Ausnahme ausgelöst, wenn der Vorgang fehlschlägt.
Die Wahl der Verwendung hängt von den Umständen ab. In erster Linie geht es darum, ob Sie bei einer fehlgeschlagenen Konvertierung eine Ausnahme auslösen möchten.
quelle
Meine Antwort bezieht sich nur auf die Geschwindigkeit in Fällen, in denen wir den Typ nicht überprüfen und nach dem Casting keine Nullen überprüfen. Ich habe Jon Skeets Code um zwei zusätzliche Tests erweitert:
Ergebnis:
Versuchen Sie nicht, sich (wie ich) auf die Geschwindigkeit zu konzentrieren, da dies alles sehr, sehr schnell ist.
quelle
as
Konvertierung (ohne Fehlerprüfung) etwa 1-3% schneller lief als das Casting (etwa 540 ms gegenüber 550 ms bei 100 Millionen Iterationen). Weder wird Ihre Bewerbung machen noch brechen.Neben all dem, was hier bereits gezeigt wurde, bin ich auf einen praktischen Unterschied gestoßen, den ich für erwähnenswert halte, zwischen explizitem Casting
versus mit dem
as
Operator.Hier ist das Beispiel:
Fazit : GenericCaster2 funktioniert nicht mit Strukturtypen. GenericCaster wird.
quelle
Wenn Sie die Office-PIAs verwenden, die auf .NET Framework 4.X abzielen, sollten Sie das Schlüsselwort as verwenden , da es sonst nicht kompiliert wird.
Das Casting ist jedoch in Ordnung, wenn Sie auf .NET 2.0 abzielen:
Beim Targeting von .NET 4.X sind die Fehler:
Fehler CS0656: Fehlender Compiler erforderlich Mitglied 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
Fehler CS0656: Fehlender Compiler erforderlich Mitglied 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
quelle
Das
as
Schlüsselwort funktioniert genauso wie eine explizite Umwandlung zwischen kompatiblen Referenztypen, mit dem Hauptunterschied, dass es keine Ausnahme auslöst, wenn die Konvertierung fehlschlägt. Vielmehr ergibt es einen Nullwert in der Zielvariablen. Da Ausnahmen in Bezug auf die Leistung sehr teuer sind, wird sie als viel bessere Gießmethode angesehen.quelle
Was Sie wählen, hängt stark davon ab, was erforderlich ist. Ich bevorzuge explizites Casting
denn wenn das Objekt vom Typ IMyInterface sein sollte und es nicht ist, ist es definitiv ein Problem. Es ist besser, Fehler so früh wie möglich zu erhalten, da der genaue Fehler behoben wird, anstatt seine Nebenwirkung zu beheben.
Wenn Sie sich jedoch mit Methoden befassen, die
object
als Parameter akzeptiert werden, müssen Sie den genauen Typ überprüfen, bevor Sie Code ausführen. In einem solchen Fallas
wäre nützlich, damit Sie vermeiden könnenInvalidCastException
.quelle
Es hängt davon ab, ob Sie nach der Verwendung von "as" nach null suchen möchten oder ob Sie es vorziehen, dass Ihre App eine Ausnahme auslöst.
Meine Faustregel lautet, wenn ich immer erwarte, dass die Variable von dem Typ ist, den ich zum Zeitpunkt der Verwendung einer Besetzung erwarte. Wenn es möglich ist, dass die Variable nicht in das umgewandelt wird, was ich möchte, und ich bereit bin, Nullen bei der Verwendung von as zu verarbeiten, verwende ich as.
quelle
Schauen Sie sich diese Links an:
Sie zeigen Ihnen einige Details und Leistungstests.
quelle
Das Problem des OP ist auf eine bestimmte Castingsituation beschränkt. Der Titel deckt viel mehr Situationen ab.
Hier ist eine Übersicht aller relevanten Castingsituationen, an die ich derzeit denken kann:
quelle