Direct casting vs 'as' โอเปอเรเตอร์?


709

พิจารณารหัสต่อไปนี้:

void Handler(object o, EventArgs e)
{
   // I swear o is a string
   string s = (string)o; // 1
   //-OR-
   string s = o as string; // 2
   // -OR-
   string s = o.ToString(); // 3
}

ความแตกต่างระหว่างการคัดเลือกสามประเภทคืออะไร (โอเคคนที่สามไม่ใช่การคัดเลือกนักแสดง แต่คุณได้รับเจตนา) ควรเลือกอันไหน


1
ค่อนข้างไม่ซ้ำกัน แต่ยังมีบางอภิปรายผลการดำเนินงานในคำถามก่อนหน้านี้
ต้อง

8
4: string s = Convert.ToString(o); วันที่ 5: string s = $"{o}"(หรือstring.Formatแบบฟอร์ม C # ก่อนหน้านี้อย่างเท่าเทียมกัน)
Earth Engine

คำตอบ:


833
string s = (string)o; // 1

พ่นInvalidCastExceptionถ้าไม่ได้เป็นo stringมิฉะนั้นกำหนดoไปsแม้ว่ามีonull

string s = o as string; // 2

จัดnullไปsถ้าoไม่ได้เป็นstringหรือถ้ามีo nullด้วยเหตุผลนี้คุณไม่สามารถใช้กับประเภทค่าได้ (ผู้ประกอบการไม่สามารถส่งคืนได้nullในกรณีนั้น) มิฉะนั้นมอบหมายไปos

string s = o.ToString(); // 3

ทำให้เกิดการNullReferenceExceptionถ้าเป็นo nullกำหนดสิ่งที่o.ToString()ส่งกลับไปถึงsไม่ว่าoจะเป็นประเภทใด


ใช้ 1 สำหรับการแปลงส่วนใหญ่ - มันง่ายและตรงไปตรงมา ฉันมักจะไม่เคยใช้ 2 เพราะถ้าสิ่งที่ไม่ใช่ประเภทที่ถูกต้องฉันมักจะคาดว่าจะเกิดข้อยกเว้น ฉันเพิ่งเห็นความต้องการฟังก์ชั่นการคืนค่าเป็นโมฆะนี้กับไลบรารีที่ออกแบบมาไม่ดีซึ่งใช้รหัสข้อผิดพลาด (เช่น return null = error แทนที่จะใช้ข้อยกเว้น)

3 ไม่ใช่นักแสดงและเป็นเพียงการเรียกใช้เมธอด ใช้สำหรับเมื่อคุณต้องการแสดงสตริงของวัตถุที่ไม่ใช่สตริง


2
คุณสามารถกำหนด 'null' ให้กับประเภทค่าเมื่อกำหนดไว้อย่างชัดเจนเช่น: int? ผม; string s = "5"; i = s เหมือน int; // i ตอนนี้ 5 s = null; i = s เหมือน int; // ตอนนี้ฉันว่าง
เปล่า

3
RE: Anheledir จริง ๆ แล้วฉันจะเป็นโมฆะหลังจากการโทรครั้งแรก คุณต้องใช้ฟังก์ชั่นการแปลงที่ชัดเจนเพื่อรับค่าของสตริง
Guvante

45
RE: Sander ที่จริงแล้วมีอีกเหตุผลที่ดีมากที่จะใช้เป็นมันลดความซับซ้อนของรหัสการตรวจสอบของคุณ (ตรวจสอบโมฆะค่อนข้างแล้วตรวจสอบโมฆะและประเภทที่ถูกต้อง) ซึ่งเป็นประโยชน์เนื่องจากหลายครั้งที่คุณค่อนข้างจะกำหนดเอง แต่มันเป็นความจริงอย่างมากที่คนตาบอดเมื่อโทรไม่ดี
Guvante

5
# 2 มีประโยชน์สำหรับสิ่งต่าง ๆ เช่นวิธีเท่ากับซึ่งคุณไม่ทราบประเภทอินพุตโดยทั่วไปแม้ว่าจะเป็นที่ต้องการ 1 แม้ว่าที่ต้องการมากกว่าที่จะเห็นได้ชัดว่าจะใช้ระบบการพิมพ์ที่จะ จำกัด ประเภทหนึ่งเมื่อคุณคาดหวังเพียงหนึ่ง :)
ไอรอน

6
# 2 ยังมีประโยชน์เมื่อคุณมีรหัสที่อาจทำบางสิ่งบางอย่างโดยเฉพาะสำหรับประเภทเฉพาะ แต่ไม่เช่นนั้นจะไม่ทำอะไรเลย
AnthonyWJones

349
  1. string s = (string)o;ใช้เมื่อสิ่งที่ควร แน่นอนเป็นสิ่งอื่น ๆ
  2. string s = o as string;ใช้เมื่อบางสิ่งบางอย่างอาจเป็นสิ่งอื่น
  3. string s = o.ToString(); ใช้เมื่อคุณไม่สนใจว่ามันคืออะไร แต่คุณแค่ต้องการใช้การแทนค่าสตริงที่มีอยู่

1
ฉันเข้าใจว่าคำตอบนี้ฟังดูดี แต่อาจไม่แม่นยำ
j riv

1
ฉันชอบสองคนแรก แต่ฉันจะเพิ่ม "และคุณแน่ใจว่ามันไม่เป็นโมฆะ" ในตัวเลือกที่สาม
Uxonith

2
คุณสามารถใช้ Elvis (?.) วันนี้เพื่อหลีกเลี่ยงการดูแล: obj
?.

@Quibblesome - คำตอบที่ดี แต่ฉันต้องหยุดคิดเกี่ยวกับการ rebuttle ของคุณ! แท้จริงแล้วมันทำให้ฉันคิดว่าภาษานั้นดีเกินกว่า 15 ปีแล้ว รู้สึกเหมือนเมื่อวานเมื่อเราทุกคน "หงุดหงิด" พยายามโน้มน้าวผู้พัฒนาระดับสูงให้เปลี่ยนเป็น C #
Griswald_911

1
@Quibblesome คำตอบที่ดี: คุณจะรำคาญถ้าฉันเพิ่มในสิ่งที่ 1/2/3 เพื่อที่ไม่จำเป็นต้องเลื่อนขึ้นเพื่อ OP ฉันด้วยดังนั้นจะจัดอันดับคำตอบเก่าตามคะแนนโหวต!
ทำไม

29

มันขึ้นอยู่กับว่าคุณรู้หรือไม่ว่าoเป็นสตริงและสิ่งที่คุณต้องการจะทำกับมัน หากความคิดเห็นของคุณหมายความว่าoจริง ๆ แล้วเป็นสตริงฉันควรเลือก(string)oนักแสดงแบบตรง- ไม่น่าจะล้มเหลว

ข้อได้เปรียบที่ใหญ่ที่สุดของการใช้นักแสดงแบบตรงคือเมื่อมันล้มเหลวคุณจะได้รับInvalidCastExceptionซึ่งจะบอกคุณได้ว่าเกิดอะไรขึ้น

ด้วยตัวasดำเนินการหากoไม่ใช่สตริงให้sตั้งค่าnullซึ่งจะสะดวกถ้าคุณไม่แน่ใจและต้องการทดสอบs:

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

อย่างไรก็ตามหากคุณไม่ทำการทดสอบคุณจะใช้ในsภายหลังและมีการโยนNullReferenceException เหล่านี้มีแนวโน้มที่จะร่วมกันมากขึ้นและมากยากที่จะติดตามลงเมื่อพวกเขาเกิดขึ้นจากในป่าเป็นเกือบทุกสาย dereferences ตัวแปรและอาจจะโยนหนึ่ง ในทางกลับกันหากคุณพยายามที่จะแปลงเป็นประเภทค่า (ดั้งเดิมใด ๆ หรือ structs เช่นDateTime ) คุณต้องใช้การโยนตรง - asจะไม่ทำงาน

ในกรณีพิเศษที่จะแปลงเป็นสตริงวัตถุทุกชิ้นมี a ToStringดังนั้นวิธีที่สามของคุณอาจไม่เป็นไรถ้าoไม่ใช่โมฆะและคุณคิดว่าToStringวิธีนั้นอาจทำในสิ่งที่คุณต้องการ


2
One note - คุณสามารถใช้asกับประเภทค่าnullable IE o as DateTimeจะไม่ทำงาน แต่o as DateTime?จะ ...
John Gibb

ทำไมไม่ใช้if (s is string)แทน
BornToCode

1
@BontToCode สำหรับฉันการตั้งค่าส่วนตัวส่วนใหญ่ ขึ้นอยู่กับสิ่งที่คุณทำบ่อยครั้งหลังจากisไอเอ็นจีคุณจะต้องทำการโยนอีกครั้งเพื่อให้คุณมีและจากนั้นนักแสดงที่ยาก ด้วยเหตุผลบางอย่างการasตรวจสอบและโมฆะทำให้ฉันรู้สึกดีขึ้น
แบลร์คอนราด

9

หากคุณรู้อยู่แล้วว่ามันสามารถใช้กับนักแสดงประเภทใดได้ให้ใช้นักแสดงสไตล์ C:

var o = (string) iKnowThisIsAString; 

โปรดทราบว่าเฉพาะกับการโยนสไตล์ C เท่านั้นที่คุณสามารถทำการข่มขู่ประเภทที่ชัดเจนได้

หากคุณไม่ทราบว่าเป็นประเภทที่ต้องการหรือไม่และคุณจะใช้หากเป็นเช่นนั้นให้ใช้เป็นคำหลัก:

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

ทราบว่าเป็นจะได้เรียกผู้ประกอบการแปลงชนิดใด ๆ มันจะไม่เป็นโมฆะถ้าวัตถุนั้นไม่เป็นโมฆะและเป็นประเภทที่ระบุ

ใช้ ToString () เพื่อรับการแสดงสตริงของวัตถุใด ๆ ที่มนุษย์สามารถอ่านได้แม้ว่าจะไม่สามารถส่งไปยังสายอักขระ


3
นั่นเป็น gotcha เล็ก ๆ ที่น่าสนใจเกี่ยวกับตัวดำเนินการแปลงชนิด ฉันมีประเภทไม่กี่ประเภทที่ฉันสร้าง Conversion ต้องระวังไว้ก่อน
AnthonyWJones

7

คำหลักในฐานะที่เป็นสิ่งที่ดีใน asp.net เมื่อคุณใช้วิธีการ FindControl

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

ซึ่งหมายความว่าคุณสามารถทำงานกับตัวแปรที่พิมพ์แล้วต้องโยนมันจากobjectแบบที่คุณต้องการด้วยการส่งโดยตรง:

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

มันไม่ใช่เรื่องใหญ่อะไร แต่มันจะบันทึกบรรทัดของรหัสและการกำหนดตัวแปรรวมทั้งสามารถอ่านได้มากขึ้น


6

'as' ขึ้นอยู่กับ 'is' ซึ่งเป็นคีย์เวิร์ดที่ตรวจสอบ ณ รันไทม์หากวัตถุนั้นเข้ากันได้กับ polimorphycally

ทั้งสองนี้เทียบเท่ากัน:

ใช้ 'เป็น':

string s = o as string;

การใช้ 'is':

if(o is string) 
    s = o;
else
    s = null;

ในทางตรงกันข้าม c-style cast ถูกสร้างขึ้นที่ runtime แต่มีข้อยกเว้นหากไม่สามารถสร้าง cast ได้

เพียงเพิ่มข้อเท็จจริงที่สำคัญ:

คำหลัก 'เป็น' จะใช้ได้กับประเภทอ้างอิงเท่านั้น คุณไม่สามารถทำ:

// I swear i is an int
int number = i as int;

ในกรณีเหล่านั้นคุณต้องใช้การคัดเลือกนักแสดง


ขอบคุณที่ชี้ความผิดของฉันคุณพูดถูก ฉันแก้ไขคำตอบ โอ๊ะขอโทษด้วย
Sergio Acosta

5

2 มีประโยชน์สำหรับการหล่อเป็นประเภทที่ได้รับ

สมมติว่าเป็นสัตว์:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

จะได้รับการเลี้ยงกับต่ำสุดของมันได้ปลดเปลื้อง


2
@Chirs Moutray นั้นเป็นไปไม่ได้เสมอโดยเฉพาะถ้าเป็นห้องสมุด
decaviatedcaviar

5

ตามการทดลองทำงานในหน้านี้: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(หน้านี้มีข้อผิดพลาด "ผู้อ้างอิงผิดกฎหมาย" ปรากฏขึ้นในบางครั้งดังนั้นให้รีเฟรชถ้าเป็นเช่นนั้น)

บทสรุปคือตัวดำเนินการ "as" โดยปกติจะเร็วกว่าการร่าย บางครั้งเร็วขึ้นหลาย ๆ ครั้งบางครั้งก็แทบเร็วขึ้น

สิ่งที่ฉัน "peronsonally" เป็นยังอ่านง่ายขึ้น

ดังนั้นเนื่องจากทั้งเร็วขึ้นและ "ปลอดภัยกว่า" (จะไม่เกิดข้อยกเว้น) และอาจจะอ่านง่ายกว่าฉันแนะนำให้ใช้ "เป็น" ตลอดเวลา


4

"(สตริง) o" จะส่งผลให้ InvalidCastException เนื่องจากไม่มีการส่งโดยตรง

"o as string" จะส่งผลให้ s เป็นข้อมูลอ้างอิงที่ว่างเปล่าแทนที่จะเป็นข้อยกเว้นที่ถูกโยนทิ้งไป

"o.ToString ()" ไม่ใช่การคัดสรรใด ๆ ต่อประเภทมันเป็นวิธีการที่นำมาใช้โดยวัตถุและในทางใดทางหนึ่งโดยทุก ๆ ชั้นใน. net ที่ "ทำอะไรบางอย่าง" กับตัวอย่างของ คลาสที่ถูกเรียกใช้และส่งคืนสตริง

อย่าลืมว่าสำหรับการแปลงเป็นสตริงนอกจากนี้ยังมี Convert.ToString (someType instanceOfThatType) ที่ someType เป็นหนึ่งในชุดประเภทหนึ่งโดยพื้นฐานแล้วประเภทพื้นฐานของเฟรมเวิร์ก


3

คำตอบที่ได้รับทั้งหมดนั้นดีถ้าฉันอาจเพิ่มบางสิ่ง: หากต้องการใช้เมธอดและคุณสมบัติของสตริงโดยตรง (เช่น ToLower) คุณไม่สามารถเขียนได้:

(string)o.ToLower(); // won't compile

คุณเขียนได้เท่านั้น:

((string)o).ToLower();

แต่คุณสามารถเขียนแทน:

(o as string).ToLower();

asตัวเลือกที่สามารถอ่านได้มากขึ้น (อย่างน้อยในความคิดของฉัน)


(o เป็นสตริง). ToLower () สร้างเอาชนะวัตถุประสงค์ของการเป็นผู้ประกอบการ สิ่งนี้จะส่งข้อยกเว้นการอ้างอิงเป็นโมฆะเมื่อ o ไม่สามารถส่งไปยังสตริงได้
james

@james - แต่ใครบอกว่าจุดประสงค์เพียงอย่างเดียวของผู้ดำเนินการในฐานะที่เป็นข้อยกเว้นถ้านักแสดงล้มเหลว? หากคุณรู้ว่า o เป็นสตริงและเพียงแค่ต้องการเขียนโค้ดทำความสะอาดคุณสามารถใช้(o as string).ToLower()แทนวงเล็บหลายอันที่สับสนได้
BornToCode

จุดประสงค์ของการเป็นค่อนข้างตรงกันข้าม - มันไม่ควรโยนข้อยกเว้นเมื่อการร่ายล้มเหลวก็ควรคืนค่าว่าง สมมุติว่า o ของคุณเป็นสตริงที่มีค่าเป็น null แล้วจะเกิดอะไรขึ้น? คำแนะนำ - การโทร ToLower ของคุณจะล้มเหลว
james

@james - ถูกต้อง แต่สิ่งที่เกี่ยวกับกรณีที่ฉันรู้แน่นอนว่ามันจะไม่เป็นโมฆะและฉันต้องทำการคัดเลือกนักแปลเพื่อให้ฉันเข้าถึงวิธีการของวัตถุนั้นได้หรือไม่
BornToCode

1
คุณสามารถทำได้อย่างแน่นอน แต่มันไม่ใช่วิธีปฏิบัติที่ดีที่สุดเพราะคุณไม่ต้องการพึ่งพาผู้โทรหรือระบบภายนอกเพื่อให้แน่ใจว่าค่าของคุณไม่เป็นโมฆะ หากคุณใช้ C # 6 คุณสามารถทำได้ (o เป็นสตริง)? ToLower ()
James

3
string s = o as string; // 2

เป็นที่ต้องการเนื่องจากเป็นการหลีกเลี่ยงการลงโทษในการคัดเลือกนักแสดง


สวัสดีคริสลิงค์ที่เป็นคำตอบคือ 404 ในตอนนี้ ... ฉันไม่แน่ใจว่าคุณมีสิ่งที่คุณต้องการแทนที่หรือไม่?
Matt

3

ดูเหมือนว่าพวกเขาทั้งสองจะมีแนวคิดที่แตกต่างกัน

หล่อโดยตรง

ประเภทไม่จำเป็นต้องเกี่ยวข้องอย่างเคร่งครัด มันมาในทุกประเภทของรสชาติ

  • การคัดเลือกนักแสดงโดยนัย / ชัดแจ้งแบบกำหนดเอง:โดยปกติจะมีการสร้างวัตถุใหม่
  • ชนิดของค่าโดยนัย:คัดลอกโดยไม่สูญเสียข้อมูล
  • Value Type Explicit: การคัดลอกและข้อมูลอาจสูญหาย
  • ความสัมพันธ์แบบ IS-A:เปลี่ยนประเภทการอ้างอิงมิฉะนั้นจะเกิดข้อยกเว้น
  • ประเภทเดียวกัน: 'การหล่อซ้ำซ้อน'

รู้สึกเหมือนว่าวัตถุจะถูกแปลงเป็นอย่างอื่น

ผู้ประกอบการเป็น

ประเภทมีความสัมพันธ์โดยตรง ในขณะที่:

  • ประเภทการอ้างอิง: IS-Aวัตถุความสัมพันธ์จะเหมือนกันเสมอเพียงแค่การเปลี่ยนแปลงการอ้างอิง
  • ชนิดค่า: คัดลอกชกมวยและ nullable ชนิด

มันให้ความรู้สึกเหมือนคุณกำลังจัดการกับวัตถุในวิธีที่แตกต่าง

ตัวอย่างและ IL

    class TypeA
    {
        public int value;
    }

    class TypeB
    {
        public int number;

        public static explicit operator TypeB(TypeA v)
        {
            return new TypeB() { number = v.value };
        }
    }

    class TypeC : TypeB { }
    interface IFoo { }
    class TypeD : TypeA, IFoo { }

    void Run()
    {
        TypeA customTypeA = new TypeD() { value = 10 };
        long longValue = long.MaxValue;
        int intValue = int.MaxValue;

        // Casting 
        TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL:  call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
        IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass  ConsoleApp1.Program/IFoo

        int loseValue = (int)longValue; // explicit -- IL: conv.i4
        long dontLose = intValue; // implict -- IL: conv.i8

        // AS 
        int? wraps = intValue as int?; // nullable wrapper -- IL:  call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
        object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
        TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
        IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo

        //TypeC d = customTypeA as TypeC; // wouldn't compile
    }

2

ฉันต้องการที่จะดึงดูดความสนใจไปที่เฉพาะเจาะจงของผู้ประกอบการเป็น :

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

โปรดทราบว่าตัวดำเนินการในขณะที่ดำเนินการอ้างอิงการแปลงการแปลง nullable และการแปลงมวย ตัวดำเนินการในฐานะไม่สามารถทำการแปลงอื่น ๆ เช่นการแปลงที่ผู้ใช้กำหนดซึ่งควรดำเนินการแทนโดยใช้นิพจน์แบบคาส


0

เมื่อพยายามรับสายอักขระของสิ่งใด ๆ ที่อาจเป็นโมฆะฉันชอบบรรทัดด้านล่างของรหัส มันมีขนาดกะทัดรัดเรียกใช้ ToString () และจัดการกับ null ได้อย่างถูกต้อง ถ้า o เป็นโมฆะ s จะมีสตริง

String s = String.Concat(o);

0

เนื่องจากไม่มีใครพูดถึงมันอินสแตนซ์ของ Java ที่เป็นคีย์เวิร์ดมากที่สุดคือ:

obj.GetType().IsInstanceOfType(otherObj)

0

ใช้การส่งโดยตรงstring s = (string) o;หากในบริบทเชิงตรรกะของแอปของคุณstringเป็นประเภทที่ถูกต้องเท่านั้น ด้วยวิธีนี้คุณจะได้รับInvalidCastExceptionและใช้หลักการของความล้มเหลวอย่างรวดเร็ว ตรรกะของคุณจะได้รับการปกป้องจากการส่งประเภทที่ไม่ถูกต้องต่อไปหรือรับ NullReferenceException หากใช้asโอเปอเรเตอร์

หากลอจิกคาดว่าจะมีหลายประเภทที่ใช้ในการร่ายstring s = o as string;และตรวจสอบnullหรือใช้isโอเปอเรเตอร์

คุณสมบัติเจ๋ง ๆ ใหม่ ๆ ปรากฎใน C # 7.0 เพื่อให้นักแสดงง่ายขึ้นและการตรวจสอบคือการจับคู่รูปแบบ :

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }

0

สนับสนุนการแปลงรูปแบบสองประเภท (แคสติ้ง) ใน C #:

|

(ประวัติย่อ

•แปลงสแตติกของ v เป็น c ในนิพจน์ที่กำหนด

•เป็นไปได้ก็ต่อเมื่อชนิดไดนามิกของ v คือ c หรือประเภทย่อยของ c

•ถ้าไม่ใช่ InvalidCastException จะถูกส่งออกไป

|

v เป็น C

•ตัวแปรที่ไม่ร้ายแรงถึง (c) v

•ดังนั้นแปลงสแตติกของ v เป็น c ในนิพจน์ที่กำหนด

•ส่งคืนค่า null ถ้าประเภทไดนามิกของ v ไม่ใช่ c หรือชนิดย่อยของ c

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