C #: Schlüsselwort 'is' und Überprüfung auf Not

287

Dies ist eine dumme Frage, aber Sie können diesen Code verwenden, um zu überprüfen, ob etwas ein bestimmter Typ ist ...

if (child is IContainer) { //....

Gibt es eine elegantere Möglichkeit, nach der Instanz "NOT" zu suchen?

if (!(child is IContainer)) { //A little ugly... silly, yes I know...

//these don't work :)
if (child !is IContainer) {
if (child isnt IContainer) { 
if (child aint IContainer) { 
if (child isnotafreaking IContainer) { 

Ja, ja ... dumme Frage ....

Da es einige Fragen dazu gibt, wie der Code aussieht, ist es nur eine einfache Rückgabe zu Beginn einer Methode.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
quelle
105
Ich persönlich mag das "Kind isnotafreaking ...". Ich stimme dafür, dass dieses Schlüsselwort in C # 5
Joseph
Ich bin interessiert zu wissen, in welcher Situation Sie dies nutzen würden. Wie sieht der "else" -Teil dieses Codes aus und können Sie den Test nicht einfach umkehren? Wenn Ihr Code sagt "Wenn das Kind kein IContainer ist, dann werfen Sie Ausnahmen aus" oder "Wenn das Kind kein IContainer ist, dann ist es vielleicht ein IFoo, also werde ich es als nächstes versuchen", gibt es dort keine implizite else-Anweisung? Mir fehlt wahrscheinlich etwas.
Martin Peck
1
@MartinPeck, möglicherweise gibt es keine else-Klausel. Das ist der Grund, warum ich danach gesucht habe.
Joshua Walsh
@MartinPeck hier ist ein Beispiel: if (!(argument is MapsControlViewModel vm)) { return; }- Ich könnte das if invertieren und den whoooole Rest der Methode in die Klammern des if setzen, aber dann würde ich Weihnachtsbaumcode mit vielen schließenden Klammern am Ende der Methode erhalten. Das ist viel weniger lesbar.
ANeves
Vielleicht brauchen wir im Allgemeinen ifnotAussagen
Dave Cousineau

Antworten:

301
if(!(child is IContainer))

ist der einzige Operator (es gibt keinen IsNotOperator).

Sie können eine Erweiterungsmethode erstellen, die dies tut:

public static bool IsA<T>(this object obj) {
    return obj is T;
}

und dann verwenden Sie es, um:

if (!child.IsA<IContainer>())

Und Sie könnten Ihrem Thema folgen:

public static bool IsNotAFreaking<T>(this object obj) {
    return !(obj is T);
}

if (child.IsNotAFreaking<IContainer>()) { // ...

Update (unter Berücksichtigung des Code-Snippets des OP):

Da Sie den Wert anschließend tatsächlich umwandeln, können Sie asstattdessen Folgendes verwenden :

public void Update(DocumentPart part) {
    part.Update();
    IContainer containerPart = part as IContainer;
    if(containerPart == null) return;
    foreach(DocumentPart child in containerPart.Children) { // omit the cast.
       //...etc...
Mehrdad Afshari
quelle
1
ck: Ich meinte im Sinne von Operatoren, es gibt nichts IsNot.
Mehrdad Afshari
5
Ja. Ich mache Witze, falls es nicht offensichtlich ist.
Mehrdad Afshari
111

Sie können es so machen:

object a = new StreamWriter("c:\\temp\\test.txt");

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
quelle
2
@Frank - yep, das Schlüsselwort is gibt einen Booleschen Wert an, den Sie mit false vergleichen können
cjk
32
@Frank funktioniert es, weil es iseine höhere Priorität hat als ==. Der einzige Grund, den Sie nicht verwenden können, !x is fist, dass es weniger Vorrang hat als !.
Mehrdad Afshari
Ich mag das, aber es scheint nicht richtig zu funktionieren, wenn eine Variable eingeführt wird, obwohl es sollte. if (a is TextReader reader == false)"sollte" funktionieren, aber Sie können die Variable nicht im wahren Pfad verwenden, da sie möglicherweise nicht initialisiert wurde.
Dave Cousineau
@ DaveCousineau - Normalerweise würden Sie eine Variable tippen und einführen, wenn Sie die eingeführte Variable verwenden möchten. Ich bin nicht sicher , wie die Variable nützlich wäre , wenn der typecheck gescheitert (Disclaimer - ich die „Pattern Matching“ -Funktion finde beide schlecht benannt und als schlecht einen Codegeruch als verwenden. outParameter)
StingyJack
@StingyJack Es gibt eine Art Panne , bei der die Variable im wahren Pfad als nicht initialisiert betrachtet wird. selbst wenn Sie sagen if (a is TextReader reader == true), dass die Variable nicht initialisiert ist.
Dave Cousineau
11

Warum nicht einfach das andere benutzen?

if (child is IContainer)
{
  //
}
else
{
  // Do what you want here
}

Es ist ordentlich, vertraut und einfach?

Mark Broadhurst
quelle
3
Daran ist nichts auszusetzen - dies ist nur eine Nitpick-Frage. Ich wollte eine Funktion sofort beenden, wenn etwas kein bestimmter Typ war. Ich habe es jetzt für immer getan (! (Kind ist etwas)), aber ich dachte, ich würde sicherstellen, dass es keinen besseren Weg gibt.
Hugoware
1
Mit dem Beispielcode in der Frage würde dies eine leere if-Klammer bedeuten. Das klingt nicht nach einer vernünftigen Alternative.
ANeves
9

Die Art und Weise, wie Sie es haben, ist in Ordnung, aber Sie könnten eine Reihe von Erweiterungsmethoden erstellen, um "eine elegantere Möglichkeit zu finden, nach der 'NOT'-Instanz zu suchen".

public static bool Is<T>(this object myObject)
{
    return (myObject is T);
}

public static bool IsNot<T>(this object myObject)
{
    return !(myObject is T);
}

Dann könnten Sie schreiben:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Robert Cartaino
quelle
7

Dies wurde noch nicht erwähnt. Es funktioniert und ich denke, es sieht besser aus als mit!(child is IContainer)

if (part is IContainer is false)
{
    return;
}

isSyntax: expr is constant wobei expr der auszuwertende Ausdruck und const der zu testende Wert ist.

Todd Skelton
quelle
3
Ebenso könnten Sie tun if (part as IContainer is null). Ehrlich gesagt nicht sicher, was besser ist.
Flynn1179
5

Hässlich? Ich stimme dir nicht zu. Der einzige andere Weg (ich persönlich finde das "hässlicher"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BKostenlos
quelle
@Mehrdad - Nullable? würde es ermöglichen zu funktionieren, nicht dass dies verwendet werden sollte. Es ist nur ein Beispiel für einen hässlicheren Weg.
Stevehipwell
@ Steveo3000: Ja, aber du solltest es ausdrücklich erwähnen? ist die asKlausel. obj as intist immer ein Fehler bei der Kompilierung.
Mehrdad Afshari
@Mehrdad - Einverstanden, BFree könnte seinen Beitrag bearbeiten, um dies widerzuspiegeln. Geben Sie uns "obj as int?".
Stevehipwell
@ Stevo3000: Ich glaube aber nicht, dass irgendetwas daran falsch ist. IContainer fühlt sich eher wie eine Schnittstelle als wie ein Wertetyp an. Ich wollte nur darauf hinweisen, dass es Sorgfalt beim Wertetyp erfordert und nicht immer eine direkte Übersetzung der isForm ist.
Mehrdad Afshari
Sie können optional if (obj == default (IContainer)) tun, das sich um Werttypen und Referenztypen kümmert
Joseph
3

Der isOperator ergibt ein boolesches Ergebnis, sodass Sie alles tun können, was Sie sonst auf einem Bool tun könnten. Um es zu negieren, verwenden Sie den !Operator. Warum sollten Sie nur dafür einen anderen Operator haben wollen?

Brian Rasmussen
quelle
5
Es ist kein anderer Operator. Ich habe mich gefragt, ob es ein Schlüsselwort gibt, mit dem ich die zusätzlichen Parens löschen kann. Es ist ein großer Trottel, aber ich war neugierig.
Hugoware
Okay ich verstehe. Durch Ihre Beispiele hatte ich den Eindruck, dass Sie nach einem neuen, engagierten Betreiber suchten.
Brian Rasmussen
Ich denke, einen so speziellen Operator zu haben ist schlecht, weil wir diesen Weg haben werden (erklärt dies jedenfalls), und wenn wir einen anderen Einsatz hatten, dann gibt es zwei Möglichkeiten, um dasselbe zu erreichen, kann verwirrend sein.
BuddhiP
3

Die Erweiterungsmethode IsNot<T>ist eine gute Möglichkeit, die Syntax zu erweitern. Merken Sie sich

var container = child as IContainer;
if(container != null)
{
  // do something w/ contianer
}

funktioniert besser als so etwas zu tun

if(child is IContainer)
{
  var container = child as IContainer;
  // do something w/ container
}

In Ihrem Fall spielt es keine Rolle, wie Sie von der Methode zurückkehren. Mit anderen Worten, achten Sie darauf, dass Sie nicht sowohl die Typprüfung als auch die Typkonvertierung unmittelbar danach durchführen.

Jeff
quelle
3

Dies vermeidet zwar nicht das Problem der Klammern, sollte jedoch erwähnt werden, dass eine neuere Syntax (ab C # 7) vorhanden ist, um den Rest Ihres Codes ein wenig übersichtlicher zu gestalten:

if (!(DocumentPart is IContainer container)) { return; }
foreach(DocumentPart child in container.Children) {
    ...

Dies vermeidet die doppelte Umwandlung, die Nullprüfung und das Vorhandensein einer Variablen in Bereichen, in denen sie null sein könnte.

StriplingWarrior
quelle
2

Während der IS-Operator normalerweise der beste Weg ist, gibt es eine Alternative, die Sie unter bestimmten Umständen verwenden können. Sie können den Operator as verwenden und auf null testen.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Muad'Dib
quelle
2

C # 9 (freigegeben wird mit .NET 5) wird die logischen Muster umfassen and, orund not, was uns dieses eleganten schreiben kann:

if (child is not IContainer) { ... }

Ebenso kann dieses Muster verwendet werden, um nach Null zu suchen:

if (child is not null) { ... }

Weitere Informationen zum Github-Problem finden Sie unter Nachverfolgung dieser Änderung.

Thorkil Holm-Jacobsen
quelle
-2
if (child is IContainer ? false : true)
Ternär
quelle