C #: „is” słowo kluczowe i sprawdzanie, czy nie

287

To głupie pytanie, ale możesz użyć tego kodu, aby sprawdzić, czy coś jest określonego typu ...

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

Czy istnieje bardziej elegancki sposób sprawdzenia wystąpienia „NIE”?

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

Tak, tak ... głupie pytanie ...

Ponieważ jest pewne pytanie , jak wygląda kod, jest to zwykły powrót na początku metody.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
źródło
105
Osobiście podoba mi się „dziecko nie jest dziwne ...”. Głosuję za umieszczeniem tego słowa kluczowego w C # 5
Joseph
Chcę poznać sytuację, w której byś tego użył? Jak wygląda część „else” tego kodu i czy nie możesz po prostu odwrócić testu? Jeśli Twój kod mówi „jeśli dziecko nie jest IContainer, to wyrzuć wyjątki” lub „jeśli dziecko nie jest IContainer, to może jest to IFoo, więc wypróbuję to dalej”, to czy nie ma tam dorozumianej instrukcji? Prawdopodobnie coś mi brakuje.
Martin Peck
1
@MartinPeck, może nie istnieć klauzula else. Właśnie dlatego tego szukałem.
Joshua Walsh
@MartinPeck oto próbka: if (!(argument is MapsControlViewModel vm)) { return; }- Mogę odwrócić if i umieścić resztę metody whoooole w nawiasach if, ale wtedy dostanę kod choinki, z dużą ilością nawiasów zamykających na końcu metody. To o wiele mniej czytelne.
ANeves
może w ogólności potrzebujemy ifnotoświadczeń
Dave Cousineau,

Odpowiedzi:

301
if(!(child is IContainer))

jest jedynym operatorem, który może przejść (nie ma IsNotoperatora).

Możesz zbudować metodę rozszerzenia, która to robi:

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

a następnie użyj go do:

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

I możesz śledzić swój temat:

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

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

Aktualizacja (z uwzględnieniem fragmentu kodu PO):

Ponieważ faktycznie rzutujesz wartość później, możesz po prostu użyć aszamiast tego:

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
źródło
1
ck: Miałem na myśli operatorów, nic nie ma IsNot.
Mehrdad Afshari
5
Tak. Żartuję na wypadek, gdyby nie było to oczywiste.
Mehrdad Afshari
111

Możesz to zrobić w ten sposób:

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

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
źródło
2
@Frank - tak, słowo kluczowe is daje wartość logiczną, którą można porównać do fałszu
cjk
32
@Frank działa, ponieważ isma wyższy priorytet w stosunku do ==. Jedynym powodem, dla którego nie możesz użyć, !x is fjest to, że ma on mniejszy priorytet niż !.
Mehrdad Afshari
Podoba mi się to, ale wydaje się, że nie działa poprawnie przy wprowadzaniu zmiennej, nawet jeśli powinno. if (a is TextReader reader == false)„powinno” działać, ale nie pozwoli ci użyć zmiennej w prawdziwej ścieżce, mówiąc, że mogła nie zostać zainicjowana.
Dave Cousineau,
@DaveCousineau - Zwykle sprawdzamy typ i wprowadzamy zmienną, gdy chcemy użyć wprowadzonej zmiennej. Nie jestem pewien, jak zmienna byłaby przydatna, gdyby próba nie powiodła się. (Zastrzeżenie - uważam, że funkcja „Wzorzec dopasowania” jest zarówno źle nazwana, jak i ma tak nieprzyjemny zapach jak używanie outparametrów)
StingyJack
@StingyJack istnieje pewien błąd, w którym na prawdziwej ścieżce zmienna jest uważana za niezainicjowaną. nawet jeśli powiesz, if (a is TextReader reader == true)że uważa, że ​​zmienna jest niezainicjowana.
Dave Cousineau
11

Dlaczego nie skorzystać z innego?

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

Czy to dobrze znane i proste?

Mark Broadhurst
źródło
3
Nie ma w tym nic złego - to tylko trafne pytanie. Chciałem natychmiast wyjść z funkcji, jeśli coś nie jest określonego typu. Zrobiłem to (! (Dziecko jest czymś)) na zawsze, ale pomyślałem, że upewnię się, że nie ma lepszego sposobu.
Hugoware
1
Z przykładowym kodem w pytaniu oznaczałoby to pusty nawias kwadratowy. To nie brzmi jak rozsądna alternatywa.
ANeves
9

Sposób, w jaki masz to jest w porządku, ale mógłby stworzyć zestaw metod rozszerzających, aby „bardziej elegancki sposób, aby sprawdzić, czy«nie», np.”

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

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

Następnie możesz napisać:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Robert Cartaino
źródło
7

Nie zostało to jeszcze wspomniane. Działa i myślę, że wygląda lepiej niż używanie!(child is IContainer)

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

isskładnia expr is constant :, gdzie wyrażenie jest wyrażeniem do oceny, a stała jest wartością do sprawdzenia.

Todd Skelton
źródło
3
Podobnie możesz zrobić if (part as IContainer is null). Szczerze mówiąc, nie jestem pewien, co jest lepsze.
Flynn1179
5

Brzydki? Nie zgadzam się. Jedyny inny sposób (osobiście uważam, że jest to „brzydsze”):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
źródło
@Mehrdad - Nullable? umożliwiłoby to jego działanie, a nie to, że należy tego użyć. To tylko przykład brzydszego sposobu.
stevehipwell
@ Steveo3000: Tak, ale należy wyraźnie wspomnieć? jest asklauzula. obj as intjest zawsze błędem czasu kompilacji.
Mehrdad Afshari
@ Mehrdad - Zgoda, BFree może edytować swój post, aby to odzwierciedlić. Dając nam „obj as int?”.
stevehipwell
@ Stevo3000: Nie sądzę jednak, żeby coś było z tym nie tak. IContainer wydaje się raczej interfejsem niż typem wartości. Chciałem tylko podkreślić, że wymaga to uwagi na typ wartości i nie zawsze jest bezpośrednim tłumaczeniem isformy.
Mehrdad Afshari
możesz opcjonalnie zrobić if (obj == default (IContainer)), który zadbałby o typy wartości i typy referencyjne
Joseph
3

Do isocenia operatorowi logiczną wyniku, więc można zrobić coś byś inaczej być w stanie zrobić na bool. Aby to zanegować, użyj !operatora. Dlaczego miałbyś chcieć mieć do tego innego operatora?

Brian Rasmussen
źródło
5
To nie jest inny operator. Zastanawiałem się, czy istnieje słowo kluczowe, które pozwoliłoby mi upuścić dodatkowy zestaw parens. To poważny wybór, ale byłem ciekawy.
Hugoware
Okej rozumiem. Z twoich przykładów mam wrażenie, że szukałeś nowego, oddanego operatora.
Brian Rasmussen
Myślę, że posiadanie takiego specjalnego operatora jest złe, ponieważ będziemy mieli taki sposób (i tak wyjaśniłem to ans), a jeśli mielibyśmy inną operację, istnieją dwa sposoby na osiągnięcie tego samego, może być mylące.
BuddhiP
3

Metoda rozszerzenia IsNot<T>jest dobrym sposobem na rozszerzenie składni. Pamiętać

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

działa lepiej niż coś takiego

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

W twoim przypadku nie ma to znaczenia, gdy wracasz z metody. Innymi słowy, należy uważać, aby nie sprawdzić zarówno typu, jak i konwersji bezpośrednio po nim.

Jeff
źródło
3

Chociaż nie pozwala to uniknąć problemu w nawiasach, dla dobra ludzi przybywających tutaj przez Google, należy wspomnieć, że istnieje nowsza składnia (od C # 7), aby resztę kodu uczynić nieco czystszą:

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

Dzięki temu unika się podwójnego rzutowania, sprawdzania wartości zerowej i posiadania zmiennej dostępnej w zakresach, w których może ona być zerowa.

StriplingWarrior
źródło
2

Chociaż operator IS jest zwykle najlepszym sposobem, istnieje alternatywa, z której można skorzystać w niektórych okolicznościach. Możesz użyć operatora as i przetestować na wartość NULL.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Muad'Dib
źródło
2

C # 9 (aby zostać zwolnione z .NET 5) obejmie logiczne wzory and, ori not, co pozwala nam napisać to bardziej elegancko:

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

Podobnie, tego wzoru można użyć do sprawdzenia wartości null:

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

Możesz znaleźć więcej szczegółów na temat problemu Github śledzącego tę zmianę.

Thorkil Holm-Jacobsen
źródło
-2
if (child is IContainer ? false : true)
Potrójny
źródło