C #: 'is' kata kunci dan memeriksa untuk Tidak

287

Ini adalah pertanyaan konyol, tetapi Anda dapat menggunakan kode ini untuk memeriksa apakah ada jenis tertentu ...

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

Apakah ada cara yang lebih elegan untuk memeriksa contoh "TIDAK"?

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

Ya, ya ... pertanyaan konyol ....

Karena ada beberapa pertanyaan tentang seperti apa kode itu, itu hanya pengembalian sederhana di awal suatu metode.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
sumber
105
Saya pribadi suka "anak yang tidak menarik ...". Saya memberikan suara untuk memasukkan kata kunci itu ke dalam C # 5
Joseph
Saya tertarik untuk mengetahui situasi yang akan Anda gunakan ini? Seperti apa bagian "lain" dari kode ini dan tidak bisakah Anda membalikkan tes saja? Jika kode Anda mengatakan "jika anak bukan IContainer lalu lepaskan pengecualian" atau "jika anak bukan IContainer maka mungkin itu IFoo jadi saya akan coba yang berikutnya" maka bukankah ada pernyataan lain yang tersirat di sana? Saya mungkin melewatkan sesuatu.
Martin Peck
1
@ MartinPeck, mungkin tidak ada klausa lain. Itulah alasan saya mencari ini.
Joshua Walsh
@ MartinPeck inilah contohnya: if (!(argument is MapsControlViewModel vm)) { return; }- Saya bisa membalikkan if dan meletakkan sisa whoooole dari metode di dalam tanda kurung if, tapi kemudian saya akan mendapatkan kode pohon-Natal, dengan banyak tanda kurung tutup di akhir metode. Itu jauh lebih mudah dibaca.
ANeves
mungkin yang kita butuhkan secara umum adalah ifnotpernyataan
Dave Cousineau

Jawaban:

301
if(!(child is IContainer))

adalah satu-satunya operator yang pergi (tidak ada IsNotoperator).

Anda dapat membangun metode ekstensi yang melakukannya:

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

dan kemudian menggunakannya untuk:

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

Dan Anda dapat mengikuti tema Anda:

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

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

Perbarui (mempertimbangkan cuplikan kode OP):

Karena Anda benar-benar memberikan nilai setelahnya, Anda bisa menggunakan assaja:

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
sumber
1
ck: Maksud saya dalam arti operator, tidak ada IsNotapa-apa.
Mehrdad Afshari
5
Iya. Saya bercanda kalau-kalau tidak jelas.
Mehrdad Afshari
111

Anda bisa melakukannya dengan cara ini:

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

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
sumber
2
@ Jujur - ya, kata kunci is is a boolean, yang dapat Anda bandingkan dengan false
cjk
32
@ Terus bekerja karena ismemiliki hak lebih tinggi relatif terhadap ==. Satu-satunya alasan Anda tidak dapat menggunakan !x is fadalah bahwa itu kurang diutamakan daripada !.
Mehrdad Afshari
Saya suka ini tetapi tampaknya tidak berfungsi dengan benar ketika memperkenalkan variabel, meskipun seharusnya. if (a is TextReader reader == false)"seharusnya" bekerja, tetapi itu tidak akan membiarkan Anda menggunakan variabel di jalur yang benar mengatakan itu mungkin belum diinisialisasi.
Dave Cousineau
@DaveCousineau - Biasanya Anda akan mengetik centang dan memperkenalkan variabel ketika Anda ingin menggunakan variabel yang diperkenalkan. Saya tidak yakin bagaimana variabel akan berguna jika kesalahan ketik gagal. (Penafian - Saya menemukan fitur "Pencocokan Pola" baik nama buruk dan seburuk bau kode menggunakan outparameter)
StingyJack
@ StingyJack ada beberapa jenis kesalahan di mana di jalur yang benar , variabel dianggap tidak diinisialisasi. bahkan jika Anda mengatakan if (a is TextReader reader == true)itu berpikir variabel tidak diinisialisasi.
Dave Cousineau
11

Mengapa tidak menggunakan yang lain saja?

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

Rapi itu akrab dan sederhana?

Mark Broadhurst
sumber
3
Tidak ada yang salah dengan itu - ini hanya pertanyaan rewel. Saya ingin segera keluar dari fungsi jika sesuatu bukan tipe tertentu. Saya sudah melakukannya (! (Anak adalah Sesuatu)) selamanya sekarang, tapi saya pikir saya akan memastikan tidak ada cara yang lebih baik.
Hugoware
1
Dengan kode sampel dalam pertanyaan, ini berarti braket if kosong. Itu tidak terdengar seperti alternatif yang masuk akal.
ANeves
9

Cara Anda memilikinya baik-baik saja tetapi Anda dapat membuat serangkaian metode ekstensi untuk membuat "cara yang lebih elegan untuk memeriksa contoh 'TIDAK'."

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

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

Maka Anda bisa menulis:

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

Ini belum disebutkan. Ini berfungsi dan saya pikir itu terlihat lebih baik daripada menggunakan!(child is IContainer)

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

issintaks expr is constant :, di mana expr adalah ekspresi untuk dievaluasi, dan konstanta adalah nilai untuk diuji.

Todd Skelton
sumber
3
Demikian pula yang bisa Anda lakukan if (part as IContainer is null). Jujur tidak yakin mana yang lebih baik.
Flynn1179
5

Jelek? Saya tidak setuju. Satu-satunya cara lain (saya pribadi berpikir ini "lebih jelek"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
sumber
@Mehrdad - Tidak dapat dibatalkan? akan memungkinkannya bekerja, bukan bahwa ini harus digunakan. Itu hanya contoh cara yang lebih buruk.
stevehipwell
@ Steveo3000: Ya, tetapi Anda harus menyebutkan secara eksplisit? adalah asklausa. obj as intselalu merupakan kesalahan waktu kompilasi.
Mehrdad Afshari
@Mehrdad - Setuju, BFree dapat mengedit posnya untuk mencerminkan ini. Memberi kami 'keberatan sebagai int?'.
stevehipwell
@ Stevo3000: Namun, saya tidak berpikir ada yang salah dengan itu. IContainer terasa seperti antarmuka daripada tipe nilai. Hanya ingin menunjukkan itu membutuhkan perhatian pada jenis nilai dan tidak selalu merupakan terjemahan langsung dari isformulir.
Mehrdad Afshari
Anda secara opsional dapat melakukannya jika (obj == default (IContainer)), yang akan menangani tipe nilai dan tipe referensi
Joseph
3

The ismengevaluasi operator untuk hasil boolean, sehingga Anda dapat melakukan apa pun yang Anda kalau tidak akan mampu melakukan pada bool. Untuk meniadakannya gunakan !operator. Mengapa Anda ingin memiliki operator yang berbeda hanya untuk ini?

Brian Rasmussen
sumber
5
Itu bukan operator yang berbeda. Saya bertanya-tanya apakah ada kata kunci yang akan membiarkan saya melepaskan set parens tambahan. Ini adalah pilihan utama, tetapi saya ingin tahu.
Hugoware
Baiklah saya mengerti. Dari contoh Anda, saya mendapat kesan bahwa Anda mencari operator baru yang berdedikasi.
Brian Rasmussen
Saya pikir memiliki operator khusus seperti itu buruk, karena kita akan memiliki cara ini (menjelaskan ans ini, toh), dan jika kita memiliki op lain, maka ada dua cara untuk mencapai hal yang sama, dapat membingungkan.
BuddhiP
3

Metode ekstensi IsNot<T>adalah cara yang bagus untuk memperluas sintaksis. Mengingat

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

melakukan lebih baik daripada melakukan sesuatu seperti

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

Dalam kasus Anda, tidak masalah karena Anda kembali dari metode. Dengan kata lain, berhati-hatilah untuk tidak melakukan keduanya untuk memeriksa jenis dan kemudian konversi jenis segera setelah.

Jeff
sumber
3

Meskipun ini tidak menghindari masalah tanda kurung, demi orang-orang yang datang ke sini melalui Google, harus disebutkan bahwa ada sintaks yang lebih baru (pada C # 7) untuk membuat kode Anda sedikit lebih bersih:

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

Ini menghindari double-cast, cek-nol, dan memiliki variabel yang tersedia dalam cakupan di mana itu bisa menjadi nol.

StriplingWarrior
sumber
2

Meskipun operator IS biasanya merupakan cara terbaik, ada alternatif yang dapat Anda gunakan dalam beberapa situasi. Anda dapat menggunakan operator as dan menguji null.

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

C # 9 (akan dirilis dengan .NET 5) akan mencakup pola logis and, ordan not, yang memungkinkan kita untuk menulis ini dengan lebih elegan:

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

Demikian juga, pola ini dapat digunakan untuk memeriksa nol:

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

Anda dapat menemukan detail lebih lanjut tentang masalah Github melacak perubahan ini.

Thorkil Holm-Jacobsen
sumber
-2
if (child is IContainer ? false : true)
Ternary
sumber