C #: 'is' คำหลักและตรวจสอบ Not


287

นี่เป็นคำถามงี่เง่า แต่คุณสามารถใช้รหัสนี้เพื่อตรวจสอบว่ามีบางอย่างที่เป็นประเภทใด ...

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

มีวิธีที่สง่างามกว่านี้ในการตรวจสอบอินสแตนซ์ "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) { 

ใช่ใช่ ... คำถามงี่เง่า ....

เนื่องจากมีคำถามบางอย่างเกี่ยวกับลักษณะของรหัสมันเป็นเพียงการคืนค่าอย่างง่าย ๆ ในตอนเริ่มต้นของวิธีการ

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

105
โดยส่วนตัวฉันชอบ "การไม่ทำสิ่งที่เด็กทำไม่ได้ ... " ฉันลงคะแนนให้ใส่คำหลักนั้นใน C # 5
Joseph

ฉันสนใจที่จะรู้ว่าคุณจะใช้สถานการณ์นี้หรือไม่? ส่วน "อื่น" ของโค้ดนี้มีลักษณะอย่างไรและคุณไม่สามารถคว่ำการทดสอบได้? หากรหัสของคุณบอกว่า "ถ้าเด็กไม่ใช่ผู้ใช้ IContainer ให้โยนข้อยกเว้น" หรือ "ถ้าเด็กไม่ใช่ผู้เลือก IContainer บางทีมันอาจจะเป็น IFoo ดังนั้นฉันจะลองต่อไป" แล้วจะไม่มีคำแถลงอื่น ๆ ฉันอาจจะหายไปบางสิ่งบางอย่าง
มาร์ติน Peck

1
@MartinPeck อาจไม่มีส่วนอื่น นั่นคือเหตุผลที่ฉันค้นหาสิ่งนี้
Joshua Walsh

@MartinPeck นี่เป็นตัวอย่าง: if (!(argument is MapsControlViewModel vm)) { return; }- ฉันสามารถสลับถ้าและวางส่วนที่เหลือของวิธีการที่อยู่ในวงเล็บของ if แต่แล้วฉันจะได้รับรหัสต้นคริสต์มาสด้วยวงเล็บจำนวนมากในตอนท้ายของวิธีการ อ่านได้น้อยกว่ามาก
Aneves

บางทีสิ่งที่เราต้องการโดยทั่วไปก็คือifnotแถลงการณ์
Dave Cousineau

คำตอบ:


301
if(!(child is IContainer))

เป็นผู้ดำเนินการรายเดียวที่จะดำเนินการ (ไม่มีIsNotผู้ดำเนินการ)

คุณสามารถสร้างวิธีการขยายที่:

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

จากนั้นใช้เพื่อ:

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

และคุณสามารถติดตามธีมของคุณ:

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

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

อัปเดต (พิจารณาข้อมูลโค้ดของ OP):

เนื่องจากคุณหล่อมูลค่าหลังจากนั้นคุณสามารถใช้asแทน:

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: ฉันหมายถึงในความหมายของผู้ประกอบการไม่มีIsNotสิ่งใด
Mehrdad Afshari

5
ใช่. ฉันล้อเล่นในกรณีที่ไม่ชัดเจน
Mehrdad Afshari

111

คุณสามารถทำได้ด้วยวิธีนี้:

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

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

2
@Frank - อ๋อเป็นคำหลักที่จะช่วยให้บูลซึ่งคุณสามารถเปรียบเทียบเท็จ
CJK

32
@Frank มันทำงานเพราะมีความสำคัญญาติที่สูงขึ้นเพื่อis ==เหตุผลเดียวที่คุณไม่สามารถใช้ก็คือว่ามันมีความสำคัญน้อยกว่า!x is f !
Mehrdad Afshari

ฉันชอบสิ่งนี้ แต่ดูเหมือนจะไม่ทำงานเมื่อแนะนำตัวแปรแม้ว่ามันควรจะเป็น if (a is TextReader reader == false)"ควร" ทำงานได้ แต่จะไม่อนุญาตให้คุณใช้ตัวแปรในเส้นทางจริงโดยบอกว่าอาจไม่ได้รับการเริ่มต้น
Dave Cousineau

@DaveCousineau - โดยปกติคุณจะพิมพ์และแนะนำตัวแปรเมื่อคุณต้องการใช้ตัวแปรที่แนะนำ ฉันไม่แน่ใจว่าตัวแปรจะมีประโยชน์อย่างไรหากการตรวจสอบประเภทล้มเหลว (ข้อจำกัดความรับผิดชอบ - ฉันพบคุณลักษณะ "การจับคู่รูปแบบ" ทั้งชื่อที่ไม่ดีและไม่ดีต่อกลิ่นรหัสเมื่อใช้outพารามิเตอร์)
StingyJack

@StingyJack มีความผิดพลาดบางอย่างซึ่งในเส้นทางที่แท้จริงตัวแปรจะถูกพิจารณาว่าเป็นค่าเริ่มต้น แม้ว่าคุณจะบอกว่าif (a is TextReader reader == true)มันคิดว่าตัวแปรนั้นไม่ได้กำหนดค่าเริ่มต้น
Dave Cousineau

11

ทำไมไม่ลองใช้อย่างอื่นล่ะ?

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

มันเป็นระเบียบที่คุ้นเคยและเรียบง่าย


3
ไม่มีอะไรผิดปกติกับมัน - นี่เป็นเพียงคำถามที่ nitpick ฉันต้องการออกจากฟังก์ชั่นทันทีหากสิ่งที่ไม่ใช่ประเภทเฉพาะ ฉันทำไปแล้ว (! (เด็กเป็นอะไร)) ตลอดไปแล้ว แต่ฉันคิดว่าฉันต้องแน่ใจว่าไม่มีวิธีที่ดีกว่านี้อีกแล้ว
Hugoware

1
ด้วยรหัสตัวอย่างในคำถามนี้จะหมายถึงการที่ว่างเปล่าถ้าวงเล็บ ไม่ได้ฟังดูเหมือนเป็นทางเลือกที่สมเหตุสมผล
Aneves

9

วิธีที่คุณมีนั้นดี แต่คุณสามารถสร้างชุดของวิธีการขยายเพื่อทำให้ "เป็นวิธีที่หรูหรากว่าในการตรวจสอบอินสแตนซ์ 'NOT'

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

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

จากนั้นคุณสามารถเขียน:

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

7

เรื่องนี้ยังไม่ได้รับการกล่าวถึง มันใช้งานได้และฉันคิดว่ามันดูดีกว่าการใช้!(child is IContainer)

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

isไวยากรณ์: expr is constantโดยที่ expr คือนิพจน์ที่ใช้ประเมินและค่าคงที่คือค่าที่จะทดสอบ


3
if (part as IContainer is null)ในทำนองเดียวกันคุณสามารถทำ จริงๆแล้วไม่แน่ใจว่าอันไหนดีกว่ากัน
Flynn1179

5

น่าเกลียด? ฉันไม่เห็นด้วย. วิธีอื่นเท่านั้น (โดยส่วนตัวฉันคิดว่านี่คือ "uglier"):

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

@Mehrdad - เป็นโมฆะ? จะเปิดใช้งานให้ทำงานไม่ใช่ว่าควรจะใช้ มันเป็นเพียงตัวอย่างของวิธีที่น่าเกลียด
stevehipwell

@ Steveo3000: ใช่ แต่คุณควรพูดถึงอย่างชัดเจน? เป็นasประโยค obj as intเป็นข้อผิดพลาดเวลารวบรวมเสมอ
Mehrdad Afshari

@Mehrdad - เห็นด้วย BFree สามารถแก้ไขโพสต์ของเขาเพื่อสะท้อนถึงสิ่งนี้ ให้ 'obj เป็น int?'
stevehipwell

@ Stevo3000: ฉันไม่คิดว่าจะมีอะไรผิดปกติกับมัน IContainer รู้สึกเหมือนเป็นอินเทอร์เฟซมากกว่าชนิดของค่า แค่ต้องการชี้ให้เห็นว่ามันต้องใช้ความระมัดระวังกับประเภทของค่าและไม่ใช่การแปลโดยตรงจากisแบบฟอร์ม
Mehrdad Afshari

คุณเลือกที่จะทำถ้า (obj == เริ่มต้น (IContainer)) ซึ่งจะดูแลค่าชนิดและประเภทอ้างอิง
โจเซฟ

3

isประเมินผู้ประกอบการที่จะเป็นผลบูลีนเพื่อให้คุณสามารถทำอะไรที่คุณมิฉะนั้นจะไม่สามารถที่จะทำในบูล เพื่อลบล้างมันใช้!ผู้ประกอบการ ทำไมคุณต้องการมีผู้ประกอบการที่แตกต่างกันเพียงแค่นี้?


5
มันไม่ใช่โอเปอเรเตอร์ที่ต่างกัน ฉันสงสัยว่ามีคำหลักที่จะให้ฉันวาง parens พิเศษหรือไม่ เป็น nit-pick ที่สำคัญ แต่ฉันอยากรู้
Hugoware

โอเคฉันเข้าใจแล้ว จากตัวอย่างของคุณฉันได้รับความประทับใจว่าคุณกำลังมองหาผู้ให้บริการรายใหม่ที่มีความทุ่มเท
Brian Rasmussen

ฉันคิดว่าการมีตัวดำเนินการพิเศษดังกล่าวไม่ดีเพราะเราจะมีวิธีนี้ (อธิบาย ans ต่อไป) และถ้าเรามี op อื่นแล้วมีสองวิธีในการบรรลุสิ่งเดียวกันอาจสร้างความสับสน
BuddhiP

3

วิธีการขยายIsNot<T>เป็นวิธีที่ดีในการขยายไวยากรณ์ เก็บไว้ในใจ

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

ทำงานได้ดีกว่าทำสิ่งที่ชอบ

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

ในกรณีของคุณมันไม่สำคัญว่าคุณจะกลับมาจากวิธีการ ในคำอื่น ๆ ระวังอย่าทำทั้งการตรวจสอบประเภทและการแปลงประเภททันทีหลังจาก


3

แม้ว่าสิ่งนี้จะไม่หลีกเลี่ยงปัญหาในวงเล็บ แต่เพื่อให้ผู้คนเดินทางมาที่นี่ผ่าน Google ได้ แต่ก็ควรจะกล่าวว่ามีไวยากรณ์ใหม่กว่า (เช่น C # 7) เพื่อทำให้โค้ดที่เหลือของคุณสะอาดขึ้นเล็กน้อย:

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

สิ่งนี้จะหลีกเลี่ยงการแสดงผลซ้ำซ้อน, การตรวจสอบค่าว่างและมีตัวแปรพร้อมใช้งานในขอบเขตที่สามารถเป็นค่าว่างได้


2

ในขณะที่ผู้ปฏิบัติงานตามปกตินั้นเป็นวิธีที่ดีที่สุด แต่ก็มีทางเลือกอื่นที่คุณสามารถใช้ได้ในบางสถานการณ์ คุณสามารถใช้เป็นโอเปอเรเตอร์และทดสอบหาค่าว่าง

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

2

C # 9 (ที่จะออกมาพร้อมกับ .NET 5) จะมีรูปแบบลอจิคัลand, orและnotซึ่งช่วยให้เราสามารถเขียนนี้มากขึ้นอย่างหรูหรา:

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

ในทำนองเดียวกันรูปแบบนี้สามารถใช้ในการตรวจสอบโมฆะ:

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

คุณสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับปัญหา Github ที่ติดตามการเปลี่ยนแปลงนี้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.