In Entwicklungsblogs, Online-Codebeispielen und (kürzlich) sogar in einem Buch stolpere ich immer wieder über Code wie diesen:
var y = x as T;
y.SomeMethod();
oder noch schlimmer:
(x as T).SomeMethod();
Das ergibt für mich keinen Sinn. Wenn Sie sicher sind, dass x
es sich um einen Typ handelt T
, sollten Sie eine direkte Besetzung verwenden : (T)x
. Wenn Sie sich nicht sicher sind, können Sie verwenden as
, müssen dies jedoch überprüfen, null
bevor Sie eine Operation ausführen. Alles, was der obige Code tut, ist, ein (nützliches) InvalidCastException
in ein (nutzloses) zu verwandeln NullReferenceException
.
Bin ich der einzige, der denkt, dass dies ein offensichtlicher Missbrauch des as
Schlüsselworts ist? Oder habe ich etwas Offensichtliches verpasst und das obige Muster macht tatsächlich Sinn?
c#
casting
type-conversion
Heinzi
quelle
quelle
((T)x).SomeMethod()
, nicht wahr? ;) (nur ein Scherz, du hast natürlich Recht!)(f as T).SomeMethod()
;)Antworten:
Dein Verständnis ist wahr. Das klingt für mich nach einem Mikrooptimierungsversuch. Sie sollten eine normale Besetzung verwenden, wenn Sie sich des Typs sicher sind. Neben der Erzeugung einer vernünftigeren Ausnahme schlägt dies auch schnell fehl. Wenn Sie falsch über Ihre Annahme über die Art, wird Ihr Programm nicht sofort und Sie werden in der Lage sein , die Ursache des Fehlers zu sehen sofort , anstatt zu warten für eine
NullReferenceException
oderArgumentNullException
oder sogar einen logischen Fehler irgendwann in der Zukunft. Im Allgemeinen ist einas
Ausdruck, auf dennull
irgendwo keine Prüfung folgt, ein Codegeruch.Wenn Sie sich bei der Besetzung nicht sicher sind und erwarten, dass sie fehlschlägt, sollten Sie sie
as
anstelle einer normalen Besetzung verwenden, die mit einemtry-catch
Block umwickelt ist . Darüber hinaus wird die Verwendung vonas
über eine Typprüfung gefolgt von einer Besetzung empfohlen. Anstatt:Verwenden Sie Folgendes, um eine
isinst
Anweisung für dasis
Schlüsselwort und einecastclass
Anweisung für die Besetzung zu generieren (wobei die Besetzung effektiv zweimal ausgeführt wird):Dies erzeugt nur eine
isinst
Anweisung. Die erstere Methode weist einen potenziellen Fehler in Multithread-Anwendungen auf, da eine Race-Bedingung dazu führen kann, dass die Variable ihren Typ ändert, nachdem dieis
Prüfung erfolgreich war und an der Cast-Linie fehlschlägt. Die letztere Methode ist für diesen Fehler nicht anfällig.Die folgende Lösung wird für die Verwendung im Produktionscode nicht empfohlen . Wenn Sie solch ein grundlegendes Konstrukt in C # wirklich hassen, können Sie in Betracht ziehen, zu VB oder einer anderen Sprache zu wechseln.
Falls jemand die Besetzungssyntax verzweifelt hasst, kann er eine Erweiterungsmethode schreiben, um die Besetzung nachzuahmen:
und verwenden Sie eine ordentliche [?] Syntax:
quelle
cache
Objekt, das ein anderer Thread versucht, es ungültig zu machen, indem er es auf setztnull
. In sperrenfreien Szenarien können solche Dinge auftreten.Object
. Die Verwendung der Methode für einen Werttyp führt dazu, dass dieser unnötig eingerahmt wird.To
Methode hier nicht sinnvoll , da er nur über die Vererbungshierarchie konvertiert, was für Werttypen ohnehin ein Boxen beinhaltet. Natürlich ist die gesamte Idee eher theoretisch als ernst.IMHO,
as
nur Sinn machen, wenn mit einemnull
Scheck kombiniert :quelle
Bei Verwendung von 'as' werden keine benutzerdefinierten Conversions angewendet, während die Besetzung sie gegebenenfalls verwendet. Das kann in einigen Fällen ein wichtiger Unterschied sein.
quelle
Ich habe hier ein bisschen darüber geschrieben:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Ich verstehe deine Meinung. Und ich stimme dem Kern zu: Ein Cast-Operator kommuniziert "Ich bin sicher, dass dieses Objekt in diesen Typ konvertiert werden kann, und ich bin bereit, eine Ausnahme zu riskieren, wenn ich falsch liege", während ein "As" -Operator kommuniziert "Ich bin nicht sicher, ob dieses Objekt in diesen Typ konvertiert werden kann. Geben Sie mir eine Null, wenn ich falsch liege."
Es gibt jedoch einen subtilen Unterschied. (x als T) .Was auch immer () kommuniziert "Ich weiß nicht nur, dass x in ein T konvertiert werden kann, sondern darüber hinaus, dass dies nur Referenz- oder Unboxing-Konvertierungen beinhaltet und dass x außerdem nicht null ist". Das kommuniziert andere Informationen als ((T) x) .Was auch immer (), und vielleicht ist es das, was der Autor des Codes beabsichtigt.
quelle
((T)x).Whatever()
auch in Verbindung steht , diex
nicht ist , [bestimmt sein] NULL, und ich hoch Zweifel daran , dass ein Autor würde typischerweise egal , ob die Umwandlung zuT
nur mit Referenz- oder unboxing Umwandlungen stattfindet, oder wenn es erfordert eine benutzerdefinierte oder Darstellung ändernden Konvertierung. Wenn ich definierepublic static explicit operator Foo(Bar b){}
, dann ist es eindeutig meine Absicht, mit derBar
ich vereinbar binFoo
. Es ist selten, dass ich diese Konvertierung vermeiden möchte.Ich habe oft Verweise auf diesen irreführenden Artikel als Beweis dafür gesehen, dass "as" schneller ist als Casting.
Einer der offensichtlicheren irreführenden Aspekte dieses Artikels ist die Grafik, die nicht angibt, was gemessen wird: Ich vermute, dass fehlgeschlagene Casts gemessen werden (wobei "as" offensichtlich viel schneller ist, da keine Ausnahme ausgelöst wird).
Wenn Sie sich die Zeit nehmen, um die Messungen durchzuführen, werden Sie feststellen, dass das Casting erwartungsgemäß schneller ist als "as", wenn das Casting erfolgreich ist.
Ich vermute, dass dies ein Grund für die Verwendung des Schlüsselworts als Frachtkult anstelle einer Besetzung ist.
quelle
Die direkte Besetzung benötigt mehr Klammern als das
as
Schlüsselwort. Selbst wenn Sie sich zu 100% sicher sind, um welchen Typ es sich handelt, wird die visuelle Unordnung verringert.Einverstanden über die Ausnahmesache. Aber zumindest für mich sind die meisten Verwendungszwecke von
as
Boil Down, umnull
danach zu prüfen , was ich schöner finde, als eine Ausnahme zu erwischen.quelle
In 99% der Fälle, in denen ich "as" verwende, bin ich mir nicht sicher, was der tatsächliche Objekttyp ist
und ich möchte keine expliziten Besetzungsausnahmen abfangen oder zweimal mit "is" besetzen:
quelle
as
. Was ist die anderen 1%?Es ist nur, weil die Leute es mögen, wie es aussieht, es ist sehr lesbar.
Seien wir ehrlich: Der Casting- / Konvertierungsoperator in C-ähnlichen Sprachen ist in Bezug auf die Lesbarkeit ziemlich schrecklich. Mir würde es besser gefallen, wenn C # entweder die Javascript-Syntax von:
Oder definieren Sie einen
to
Operator, dessen Casting-Äquivalentas
:quelle
dynamic_cast<>()
(und ähnlichem) zu verwenden. Du machst etwas Hässliches, es sollte hässlich aussehen.Die Leute mögen es
as
so sehr, weil sie sich dadurch vor Ausnahmen sicher fühlen ... Wie eine Garantie auf eine Schachtel. Ein Mann gibt eine ausgefallene Garantie auf die Schachtel, weil er möchte, dass Sie sich im Inneren warm und wohlig fühlen. Sie glauben, Sie haben diese kleine Schachtel nachts unter Ihr Kissen gelegt, die Garantiefee könnte herunterkommen und ein Viertel verlassen, habe ich Recht, Ted?Zurück zum Thema ... Wenn Sie eine direkte Besetzung verwenden, besteht die Möglichkeit einer ungültigen Besetzungsausnahme. Menschen wenden sich daher
as
als umfassende Lösung für alle ihre Casting-Anforderungen an, da sieas
(für sich genommen) niemals eine Ausnahme auslösen werden. Aber das Lustige daran ist, dass Sie in dem Beispiel, das(x as T).SomeMethod();
Sie gegeben haben, eine ungültige Besetzungsausnahme gegen eine Nullreferenzausnahme eintauschen. Was das eigentliche Problem verschleiert, wenn Sie die Ausnahme sehen.Ich benutze im Allgemeinen nicht
as
zu viel. Ich bevorzuge denis
Test, weil er mir lesbarer und sinnvoller erscheint, als eine Besetzung zu versuchen und nach Null zu suchen.quelle
as
als pauschale Lösung bewerben, weil sie sich dadurch sicher fühlen.Dies muss einer meiner Top-Ärmel sein .
In Stroustrups D & E und / oder einem Blog-Beitrag, den ich derzeit nicht finden kann, wird der Begriff eines
to
Operators erörtert, der den von https://stackoverflow.com/users/73070/johannes-rossel gemachten Punkt ansprechen würde (dh dieselbe Syntax wie,as
aber mitDirectCast
Semantik) ).Der Grund, warum dies nicht implementiert wurde, ist, dass ein Cast Schmerzen verursachen und hässlich sein sollte, sodass Sie davon abgehalten werden, ihn zu verwenden.
Schade, dass "clevere" Programmierer (oft Buchautoren (Juval Lowy IIRC)) dies umgehen, indem sie
as
auf diese Weise missbrauchen (C ++ bietetas
wahrscheinlich aus diesem Grund keine an ).Auch hat VB mehr Konsistenz eine einheitliche Syntax gekennzeichnet, dass zwingt Sie ein wählen ,
TryCast
oderDirectCast
und Make up your mind !quelle
DirectCast
Verhalten , nicht Syntax .semantics
stattdessen zu verwenden: Pdouble-to-int
Besetzung erkennen, die fehlschlagen würde, wenndouble
sie keinen exakten Wert darstellt, der in eine passen könnteInt32
, aber(int)-1.5
Ausbeute -1 zu haben, ist einfach hässlich.MaybeValid<T>
mit zwei öffentlichen Feldern zu habenIsValid
undValue
welchen Code es nach eigenem Ermessen tun könnte. Das hätte zB erlaubtMaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }
. Dies würde nicht nur mindestens zwei Kopiervorgänge im Vergleich zu sparenNullable<T>
, sondern es könnte sich auch für jeden TypT
lohnen - nicht nur für Klassen.Ich glaube, dass das
as
Schlüsselwort als eine elegantere Version vondynamic_cast
aus C ++ angesehen werden könnte.quelle
dynamic_cast
C ++.std::bad_cast
.static_cast
keine Laufzeitüberprüfung durch. In C # gibt es keine ähnliche Besetzung.Es ist wahrscheinlich ohne technischen Grund beliebter, sondern nur, weil es einfacher zu lesen und intuitiver ist. (Nicht zu sagen, dass es besser ist, nur zu versuchen, die Frage zu beantworten)
quelle
Ein Grund für die Verwendung von "as":
Anstelle von (schlechter Code):
quelle
obj
würde bedeuten, dieobj
Variable selbst so zu ändern , dass sie einen Verweis auf ein anderes Objekt enthält. Es würde den Inhalt des Speichers nicht ändern, in dem sich das Objekt befindet, auf das ursprünglich verwiesen wurdeobj
. Dieses ursprüngliche Objekt würde unverändert bleiben und diet
Variable würde immer noch einen Verweis darauf enthalten.