C #: 'è' parola chiave e verifica la presenza di Not


288

Questa è una domanda stupida, ma puoi usare questo codice per verificare se qualcosa è un tipo particolare ...

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

Esiste un modo più elegante per verificare l'istanza "NOT"?

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) { 

Sì, sì ... domanda stupida ....

Perché c'è qualche domanda su come appare il codice, è solo un semplice ritorno all'inizio di un metodo.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...

105
Personalmente mi piace il "bambino che non sta aprendo ...". Sto votando per inserire quella parola chiave in C # 5
Joseph,

Sono interessato a sapere la situazione in cui lo useresti? Che aspetto ha la parte "else" di questo codice e non puoi semplicemente invertire il test? Se il tuo codice dice "se il bambino non è un IContainer, allora getta eccezioni" o "se il bambino non è un IContainer, allora forse è un IFoo, quindi ci proverò dopo", allora non c'è un'affermazione implicita lì? Probabilmente mi manca qualcosa.
Martin Peck,

1
@MartinPeck, potrebbe non esserci un'altra clausola. Questo è il motivo per cui ho cercato questo.
Joshua Walsh,

@MartinPeck ecco un esempio: if (!(argument is MapsControlViewModel vm)) { return; }- Potrei invertire il resto if e mettere il resto del metodo tra parentesi if, ma poi otterrei il codice dell'albero di Natale, con un sacco di parentesi di chiusura alla fine del metodo. È molto meno leggibile.
ANeves

forse ciò di cui abbiamo bisogno in generale sono le ifnotdichiarazioni
Dave Cousineau,

Risposte:


301
if(!(child is IContainer))

è l'unico operatore ad andare (non c'è nessun IsNotoperatore).

È possibile creare un metodo di estensione che lo faccia:

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

e poi usalo per:

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

E potresti seguire il tuo tema:

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

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

Aggiornamento (considerando lo snippet di codice del PO):

Dal momento che stai effettivamente lanciando il valore in seguito, puoi semplicemente usare asinvece:

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...

1
ck: intendevo nel senso di operatori, non c'è IsNotniente.
Mehrdad Afshari,

5
Sì. Sto scherzando nel caso in cui non sia ovvio.
Mehrdad Afshari,

111

Puoi farlo in questo modo:

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

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}

2
@Frank - sì, la parola chiave è un valore booleano, che puoi confrontare con false
cjk

32
@Frank funziona perché isha una precedenza maggiore rispetto a ==. L'unica ragione per cui non puoi usare !x is fè che ha meno precedenza di !.
Mehrdad Afshari,

Mi piace, ma non sembra funzionare bene quando si introduce una variabile, anche se dovrebbe. if (a is TextReader reader == false)"dovrebbe" funzionare, ma non ti permetterà di usare la variabile nel vero percorso dicendo che potrebbe non essere stata inizializzata.
Dave Cousineau,

@DaveCousineau: normalmente verifichi il controllo e introduci una variabile quando desideri utilizzare la variabile introdotta. Non sono sicuro di come la variabile sarebbe utile se il typecheck fallisse (disclaimer - trovo che la funzione "Pattern Matching" sia mal nominata e con un cattivo odore di codice rispetto all'uso dei outparametri)
StingyJack

@StingyJack c'è qualche tipo di glitch in cui nel percorso vero la variabile è considerata non inizializzata. anche se dici if (a is TextReader reader == true)che pensa che la variabile non sia inizializzata.
Dave Cousineau,

11

Perché non usare semplicemente l'altro?

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

È pulito, familiare e semplice?


3
Non c'è niente di sbagliato in questo - questa è solo una domanda nitida. Volevo uscire immediatamente da una funzione se qualcosa non era un tipo particolare. L'ho fatto (! (Child is Something)) per sempre, ma ho pensato di assicurarmi che non esistesse un modo migliore.
Hugoware,

1
Con il codice di esempio nella domanda, ciò significherebbe una parentesi vuota se. Non sembra un'alternativa ragionevole.
ANeves

9

Il modo in cui lo avete va bene, ma si potrebbe creare una serie di metodi di estensione per fare "un modo più elegante per verificare la 'NON E' istanza."

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

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

Quindi potresti scrivere:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}

8

Questo non è stato ancora menzionato. Funziona e penso che appaia meglio dell'uso!(child is IContainer)

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

issintassi expr is constant :, dove expr è l'espressione da valutare e costante è il valore da verificare.


3
Allo stesso modo potresti fare if (part as IContainer is null). Onestamente non sono sicuro di quale sia il migliore.
Flynn1179,

5

Brutta? Non sono d'accordo. L'unico altro modo (penso personalmente che sia "più brutto"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}

@Mehrdad - Nullable? gli permetterebbe di funzionare, non che questo dovrebbe essere usato. È solo un esempio di un modo più brutto.
stevehipwell,

@ Steveo3000: Sì, ma dovresti menzionare esplicitamente? è la asclausola. obj as intè sempre un errore di compilazione.
Mehrdad Afshari,

@Mehrdad - D'accordo, BFree potrebbe modificare il suo post per riflettere questo. Dandoci "obj as int?".
stevehipwell,

@ Stevo3000: Non penso che ci sia nulla di sbagliato in questo, tuttavia. IContainer sembra un'interfaccia piuttosto che un tipo di valore. Volevo solo sottolineare che richiede attenzione sul tipo di valore e non è sempre una traduzione diretta della isforma.
Mehrdad Afshari,

si potrebbe facoltativamente fare if (obj == default (IContainer)), che si occuperebbe dei tipi di valore e dei tipi di riferimento
Joseph,

3

L' isoperatore valuta un risultato booleano, quindi puoi fare tutto ciò che altrimenti potresti fare su un booleano. Per negarlo usa l' !operatore. Perché vorresti avere un operatore diverso solo per questo?


5
Non è un operatore diverso. Mi chiedevo se esistesse una parola chiave che mi permettesse di eliminare il set extra di parentesi. È una scelta importante, ma ero curioso.
Hugoware,

Ok capisco. Dai tuoi esempi ho avuto l'impressione che stavi cercando un nuovo operatore dedicato.
Brian Rasmussen,

Penso che avere un operatore così speciale sia negativo, perché avremo questo modo (spiegato questo ans, comunque), e se avessimo un'altra operazione, allora ci sono due modi per ottenere la stessa cosa, può essere fonte di confusione.
BuddhiP,

3

Il metodo di estensione IsNot<T>è un buon modo per estendere la sintassi. Tieni a mente

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

si comporta meglio di fare qualcosa di simile

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

Nel tuo caso, non importa come stai tornando dal metodo. In altre parole, fare attenzione a non eseguire sia la verifica del tipo che la conversione del tipo immediatamente dopo.


3

Sebbene ciò non eviti il ​​problema delle parentesi, per il bene delle persone che arrivano qui tramite Google, va menzionato che esiste una sintassi più recente (a partire da C # 7) per rendere il resto del codice un po 'più pulito:

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

Ciò evita il doppio cast, il controllo null e la disponibilità di una variabile in ambiti in cui potrebbe essere null.


2

Mentre l'operatore IS è normalmente il modo migliore, esiste un'alternativa che è possibile utilizzare in alcune circostanze. È possibile utilizzare l'operatore as e testare null.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}

2

C # 9 (per essere rilasciato con .NET 5) includerà i modelli logici and, ore not, che ci permette di scrivere questo più elegante:

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

Allo stesso modo, questo modello può essere utilizzato per verificare la presenza di null:

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

Puoi trovare maggiori dettagli sul problema di Github che tiene traccia di questa modifica.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.