คำตอบด้านล่างบรรทัดถูกเขียนในปี 2008
แนะนำการจับคู่รูปแบบ C # 7 ซึ่งแทนที่ตัวas
ดำเนินการส่วนใหญ่ตามที่คุณสามารถเขียน:
if (randomObject is TargetType tt)
{
// Use tt here
}
โปรดทราบว่าtt
ยังคงอยู่ในขอบเขตหลังจากนี้ แต่ไม่ได้รับการกำหนดแน่นอน (มันถูกกำหนดภายในif
ร่างกายอย่างแน่นอน) มันน่ารำคาญเล็กน้อยในบางกรณีดังนั้นถ้าคุณสนใจแนะนำจำนวนตัวแปรที่เล็กที่สุดเท่าที่จะเป็นไปได้ในทุก ๆ ขอบเขตคุณอาจต้องการใช้is
ตามด้วยตัวละคร
ฉันไม่คิดว่าคำตอบใด ๆ (ในขณะที่เริ่มคำตอบนี้!) ได้อธิบายจริงๆว่ามันคุ้มค่ากับการใช้งานที่ไหน
อย่าทำสิ่งนี้:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
การตรวจสอบนี้ไม่เพียงสองครั้ง แต่อาจตรวจสอบสิ่งต่าง ๆ หากrandomObject
เป็นเขตข้อมูลแทนที่จะเป็นตัวแปรเฉพาะที่ เป็นไปได้ที่ "ถ้า" จะผ่าน แต่จากนั้นการโยนจะล้มเหลวหากเธรดอื่นเปลี่ยนค่าrandomObject
ระหว่างสอง
ถ้าrandomObject
มันควรจะเป็นตัวอย่างของการTargetType
เช่นถ้ามันไม่ได้หมายความว่ามีข้อผิดพลาดแล้วหล่อเป็นโซลูชั่นที่เหมาะสม นั่นทำให้เกิดข้อผิดพลาดในทันทีซึ่งหมายความว่าจะไม่ทำงานอีกต่อไปภายใต้สมมติฐานที่ไม่ถูกต้องและข้อยกเว้นจะแสดงประเภทของบั๊กอย่างถูกต้อง
// This will throw an exception if randomObject is non-null and
// refers to an object of an incompatible type. The cast is
// the best code if that's the behaviour you want.
TargetType convertedRandomObject = (TargetType) randomObject;
หากrandomObject
อาจเป็นตัวอย่างของTargetType
และTargetType
เป็นประเภทอ้างอิงให้ใช้รหัสดังนี้:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
หากrandomObject
อาจเป็นตัวอย่างของTargetType
และTargetType
เป็นประเภทค่าเราไม่สามารถใช้as
กับTargetType
ตัวเองได้ แต่เราสามารถใช้ประเภทที่ไม่สามารถใช้ได้:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(หมายเหตุ: ปัจจุบันนี้ช้ากว่า + castจริง ๆ แล้วฉันคิดว่ามันดูดีและมีความสอดคล้องกันมากขึ้น แต่เราไปกันแล้ว)
หากคุณไม่ต้องการค่าที่แปลง แต่คุณต้องรู้ว่าเป็นตัวอย่างของ TargetType หรือไม่is
ผู้ดำเนินการคือเพื่อนของคุณ ในกรณีนี้มันไม่สำคัญว่า TargetType เป็นประเภทการอ้างอิงหรือประเภทค่า
อาจมีกรณีอื่น ๆ ที่เกี่ยวข้องกับยาชื่อสามัญที่is
มีประโยชน์ (เพราะคุณอาจไม่รู้ว่า T เป็นประเภทอ้างอิงหรือไม่ดังนั้นคุณจึงไม่สามารถใช้เป็นได้) แต่มันค่อนข้างคลุมเครือ
ฉันเกือบจะใช้แน่นอนis
สำหรับกรณีประเภทค่าก่อนหน้านี้ตอนนี้ไม่ได้คิดถึงการใช้ประเภท nullable และas
ร่วมกัน :)
แก้ไข: โปรดทราบว่าไม่มีการพูดถึงข้างต้นเกี่ยวกับประสิทธิภาพนอกเหนือจากกรณีประเภทค่าที่ฉันได้สังเกตเห็นว่าการยกเลิกการทำกล่องให้เป็นประเภทค่าที่ไม่สามารถใช้งานได้จริงช้ากว่า - แต่ก็สอดคล้องกัน
ตามคำตอบของ naasking นั้นเป็น and-cast หรือ is-and-as ทั้งสองมีความรวดเร็วเท่ากับ as-and-null-check กับ JIT ที่ทันสมัยดังแสดงในรหัสด้านล่าง:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
ในแล็ปท็อปของฉันสิ่งเหล่านี้ทำงานในเวลาประมาณ 60ms สองสิ่งที่ควรทราบ:
- ไม่มีความแตกต่างอย่างมีนัยสำคัญระหว่างพวกเขา (ในความเป็นจริงมีสถานการณ์ที่เป็นบวก null ตรวจสอบแน่นอนคือช้าลงรหัสข้างต้นจริงทำให้ประเภทการตรวจสอบง่ายเพราะมันเป็นสำหรับการเรียนการประทับตรา. ถ้าคุณตรวจสอบกำลังสำหรับอินเตอร์เฟซและเคล็ดลับความสมดุลเล็กน้อย ในความโปรดปรานของ as-plus-null-check.)
- พวกเขาทั้งหมดเร็วเมามัน สิ่งนี้จะไม่เป็นคอขวดในรหัสของคุณเว้นแต่ว่าคุณจะไม่ทำอะไรกับค่าในภายหลัง
ดังนั้นไม่ต้องกังวลกับประสิทธิภาพ มากังวลเรื่องความถูกต้องและความสม่ำเสมอกันเถอะ
ฉันยืนยันว่า is-and-cast (หรือ is-and-as) ทั้งคู่ไม่ปลอดภัยเมื่อจัดการกับตัวแปรเนื่องจากชนิดของค่าที่อ้างถึงอาจเปลี่ยนแปลงได้เนื่องจากเธรดอื่นระหว่างการทดสอบและนักแสดง นั่นจะเป็นสถานการณ์ที่ค่อนข้างหายาก - แต่ฉันอยากจะมีการประชุมที่ฉันสามารถใช้ได้อย่างสม่ำเสมอ
ฉันยังคงยืนยันว่าการตรวจสอบตามมาเป็นโมฆะจะช่วยแยกข้อกังวลได้ดียิ่งขึ้น เรามีหนึ่งคำสั่งที่พยายามแปลงแล้วหนึ่งคำสั่งที่ใช้ผลลัพธ์ is-and-cast หรือ is-and-as ทำการทดสอบแล้วลองอีกครั้งเพื่อแปลงค่า
หากต้องการกล่าวอีกวิธีหนึ่งไม่มีใครเคยเขียน:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
นั่นคือสิ่งที่ - และ - โยนกำลังทำ - แม้ว่าจะเห็นได้ชัดว่าค่อนข้างถูกกว่า