ไม่สามารถใช้อาร์เรย์“ อินไลน์” ใน C # ได้หรือไม่?


92

ลองนึกภาพคุณมีที่นี่

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

หรือแม้แต่แค่นี้

public static string OneOf(this string[] strings)
    {
    return "a";
    }

แล้วแน่นอนคุณสามารถทำได้ ...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

... ซึ่งเยี่ยมมาก แต่. ดูเหมือนว่าคุณไม่สามารถทำได้:

string letter = {"a","b","c"}.AnyOne();

หรืออาจเป็นเช่นนี้

string letter = ( {"a","b","c"} ).AnyOne();

หรืออย่างอื่นที่ฉันพยายาม

ในความเป็นจริง (1) ทำไมถึงทำอย่างนั้นไม่ได้? และ (2) ฉันขาดอะไรไปคุณจะทำอย่างไรถ้ามีวิธี?


5
ฉันไม่แน่ใจว่าคำถามที่ซ้ำกันนั้นเหมาะสม OP ไม่ได้ถามเกี่ยวกับตัวเริ่มต้นอาร์เรย์ แต่ทำไมคอมไพเลอร์ไม่รู้จักวัตถุเป็นอาร์เรย์จนกว่าจะมีการกำหนด
Ron Beyer

4
ฉันไม่คุ้นเคยกับ C # คำศัพท์ แต่ผมเชื่อว่านี่เป็นมากกว่าปกติที่เรียกว่าอักษรหรืออาร์เรย์ที่แท้จริงมากกว่าแบบอินไลน์
ไค

3
องค์ประกอบทางวากยสัมพันธ์นั้นเป็นตัวเริ่มต้นอาร์เรย์หรือตัวเริ่มต้นการรวบรวมขึ้นอยู่กับบริบทที่ใช้ ในกรณีที่ไม่เป็นมันจัดเป็นการแสดงออก
Eric Lippert

คำตอบ:


133

คุณต้องสร้างอาร์เรย์ก่อนโดยใช้new[].

string letter = (new[] {"a","b","c"}).AnyOne();

ดังที่ @hvd กล่าวไว้ว่าคุณสามารถทำได้โดยไม่ต้องใช้ parantheses (..)ฉันเพิ่ม parantheses เพราะฉันคิดว่ามันอ่านง่ายขึ้น

string letter = new[] {"a","b","c"}.AnyOne();

และคุณสามารถระบุประเภทข้อมูลได้new string[]ตามที่มีการกล่าวถึงคำตอบอื่น ๆ


คุณไม่สามารถทำได้{"a","b","c"}เพราะคุณคิดว่ามันเป็นวิธีเติมอาร์เรย์ไม่ใช่เพื่อสร้างอาร์เรย์

อีกสาเหตุหนึ่งที่ทำให้คอมไพเลอร์สับสนไม่รู้จะสร้างอะไรเช่น a string[]{ .. }หรือList<string>{ .. }.

การใช้new[]คอมไพเลอร์เพียงอย่างเดียวสามารถรู้ได้ตามชนิดข้อมูล ( "..") ระหว่าง{..}สิ่งที่คุณต้องการ ( string) ส่วนสำคัญคือ[]นั่นหมายความว่าคุณต้องการอาร์เรย์

คุณไม่สามารถสร้างอาร์เรย์ว่างด้วยnew[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
คุณไม่จำเป็นต้องมีวงเล็บเหล่านั้น string letter = new[] {"a","b","c"}.AnyOne();เป็นเรื่องปกติ ถ้าคุณต้องการถ้าคุณคิดว่ามันอ่านได้ง่ายกว่าด้วยวงเล็บแสดงว่าใช้ได้ แต่ในกรณีนั้นฉันคิดว่าอย่างน้อยก็ควรค่าแก่การกล่าวถึงว่านี่เป็นทางเลือกที่ใส่ใจในส่วนของคุณโดยที่ภาษาไม่ได้บังคับ

ฉันรู้เกี่ยวกับไวยากรณ์ใหม่ [] {1,2} แต่มีไวยากรณ์ที่ง่ายกว่านี้ไหม บางอย่างเช่น [1, 2]?
seguso

51

(1) ทำไมถึงทำอย่างนั้นไม่ได้? {"a","b","c"}.AnyOne();

บรรทัดนี้:

string[] st = {"a","b","c"};

เป็นมือสั้นสำหรับนิพจน์การสร้างอาร์เรย์ที่ เท่ากัน(ภายใต้ILSpy )

string[] st = new string[]  {"a","b","c"};

นี้string[] st = {"a","b","c"} สามารถนำมาใช้เฉพาะในช่วงเวลาของการประกาศคุณไม่สามารถได้ใช้มันที่อื่น ๆ ที่คุณไม่สามารถแม้แต่จะทำ:

string[] st;
st = {"a", "b", "c"}; //Error

ได้อธิบายไว้ภายใต้หัวข้อ 7.6.10.4 สำหรับนิพจน์การสร้างอาร์เรย์ในข้อกำหนดภาษา C #

ดังนั้นสิ่งนี้"{"a", "b", "c"}"เพียงอย่างเดียวโดยไม่ต้องใช้ในการประกาศจึงไม่มีความหมายอะไร ดังนั้นคุณจึงไม่สามารถใช้กับวิธีการขยายของคุณได้เนื่องจากวิธีการขยายของคุณทำงานบนอาร์เรย์

(2) ฉันขาดอะไรไปคุณจะทำอย่างไรถ้ามีวิธี?

กล่าวไว้แล้วในคำตอบของ @ adricadar คุณสามารถทำได้:

(new[] {"a","b","c"}).AnyOne();

หรือ

(new string[] {"a","b","c"}).AnyOne();

48

ฉันกลับไปตอบคำถาม "ทำไมไม่" เพราะอย่างแรกคำตอบแทบจะไม่น่าพอใจเลย - คุณได้รับคำตอบแล้ว "ฟีเจอร์ไม่ได้เป็นอย่างที่คุณต้องการเพราะข้อกำหนดไม่ได้บอกว่าคุณต้องการให้พูดอะไร" ซึ่งฉันคิดว่าไม่ใช่คำตอบที่น่าพอใจเป็นพิเศษ ประการที่สองทีมออกแบบไม่จำเป็นต้องให้เหตุผลว่าทำไมโลกถึงไม่เป็นอย่างที่คุณต้องการ ไม่มีคุณลักษณะฟรีและได้รับการออกแบบจากภาษา คุณลักษณะต่างๆจะต้องได้รับการพิสูจน์ก่อนแล้วจึงออกแบบมา

ดังนั้นเรามาลองทำให้คำถาม "ทำไมไม่" ของคุณชัดเจนขึ้นอีกนิด คุณลักษณะที่มีอยู่คือ "อาจใช้ตัวเริ่มต้นอาร์เรย์ (a) ทางด้านขวาของค่าเท่ากับในการเริ่มต้นหรือ (b) ทางด้านขวาของการสร้างวัตถุประเภทอาร์เรย์" คุณลักษณะที่เสนอคือ: "ตัวเริ่มต้นอาร์เรย์อาจใช้เป็นนิพจน์ได้" คำถามคือ "เอริคจะวิพากษ์วิจารณ์อะไรเกี่ยวกับคุณลักษณะที่นำเสนอ"

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

ใน C # 1.0 เมื่อมีการเพิ่มคุณสมบัตินี้มีการอนุมานประเภทศูนย์ทั้งหมดที่เกิดขึ้นในภาษา หลักการออกแบบในยุคแรก ๆ ของ C # คือ "ไม่น่าประหลาดใจ" และคอมไพเลอร์ก็ไม่ "ฉลาดเกินไป" หากผู้พัฒนาตั้งใจให้นิพจน์เป็นประเภทใดประเภทหนึ่งประเภทนั้นควรจะชัดเจนในนิพจน์ เมื่อคุณพูดว่า

new double[] { 1, 2, 3.4 }

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

new Animal[] { cat, dog, null }

คุณลักษณะที่นำเสนอละเมิดหลักการนี้ นิพจน์ต้องมีประเภท แต่ไม่ชัดเจนว่าอาร์กิวเมนต์อยู่ในประเภทใด

M({cat, dog, null})

นอกจากนี้: สมมติว่าเรามีสองทับถมของMหนึ่งซึ่งใช้เวลาอาร์เรย์ของและหนึ่งซึ่งใช้เวลาอาร์เรย์ของAnimal IPetใช้งานเกินพิกัดMใด หนึ่งใน Conversion ดีกว่าอีกรายการหรือไม่? ประเภทขององค์ประกอบคือCatและDog; มันสมเหตุสมผลหรือไม่ที่จะอนุมานประเภทที่ไม่ได้ปรากฏที่นั่น? คำถามเหล่านี้ล้วนเป็นคำถามที่ทีมออกแบบต้องพิจารณาและเป็นคำถามที่ไม่มีคำตอบที่ชัดเจน คุณลักษณะที่เสนอนำเราไปสู่ห้วงน้ำลึกในระยะสั้น ๆ

ตอนนี้ C # 3.0 แก้ปัญหานี้ได้เนื่องจาก C # 3.0 ได้เพิ่มคุณสมบัติมากมายที่คอมไพเลอร์สรุปประเภทในนามของผู้พัฒนา หลักการก่อนหน้านี้เกี่ยวกับ "ไม่เซอร์ไพรส์" และ "กฎง่ายๆ" ขัดแย้งกับหลักการออกแบบอื่น ๆ ที่จำเป็นในการทำให้ LINQ ทำงานได้ ควรเพิ่มคุณลักษณะที่คุณเสนอใน C # 3.0 หรือไม่?

มันอาจจะเป็น คุณลักษณะที่เพิ่มเข้ามาใน C # 3.0 คือ:

new[] { x, y, z }

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

คุณลักษณะนั้นอาจผ่อนคลายมากขึ้นเพื่อให้เป็นnew[]ทางเลือก สิ่งนี้ไม่ได้ทำ

ตอนนี้ถ้าคุณถามฉันในกรอบเวลา C # 3.0 เพื่อวิจารณ์คุณลักษณะที่นำเสนอฉันจะชี้ให้เห็นว่า (1) คอมไพเลอร์ C # 3.0 ตกอยู่ในอันตรายร้ายแรงจากการเลื่อนกำหนดการสำหรับการเปิดตัวทั้งหมดดังนั้นอย่าเพิ่มอีกต่อไป ภาระการออกแบบการใช้งานและการทดสอบสำหรับคุณลักษณะที่ไม่จำเป็นอย่างละเอียดซึ่งช่วยให้ผู้ใช้ประหยัดการกดแป้นพิมพ์ได้หกครั้งและ (2) C # 3.0 ยังเพิ่มตัวเริ่มต้นการรวบรวม:

new List<int>() { 10, 20, 30 }

เหตุผลที่ควร{10, 20, 30}จะเป็นโดยอัตโนมัติอาร์เรย์ ? ทำไมไม่ควรเป็นList<int>? หรือประเภทอื่น ๆ ทำไมถึงมีอคติต่ออาร์เรย์ โปรดจำไว้ว่าเมื่อเราเลือกที่จะประดิษฐานไวยากรณ์สำหรับอาร์เรย์เราจะติดอยู่กับมันตลอดไป อาจไม่เป็นอย่างอื่นดังนั้นคุณลักษณะที่นำเสนอจึงไม่เพียง แต่ไม่จำเป็นเท่านั้น แต่ยังป้องกันคุณลักษณะในอนาคตที่ดูเป็นไปได้อีกด้วย

สรุป: คุณลักษณะที่นำเสนอนั้นละเมิดหลักการออกแบบบางประการของ C # 1.0 โดยตรง เป็นการเพิ่มภาระให้กับ C # 3.0 โดยไม่จำเป็น ในทุกเวอร์ชันของภาษาตั้งแต่ C # 3.0 คุณลักษณะที่นำเสนอนี้ไม่มีข้อโต้แย้งที่ดีที่จะแนะนำให้ใช้เวลาความพยายามและเงินกับคุณลักษณะที่คุ้มค่าอื่น ๆ อีกมากมาย

ดังนั้นจึงไม่มีคุณสมบัติดังกล่าว


คุณต้องซื้อเสื้อยืดที่พิมพ์ว่า "โลกไม่ใช่แบบที่คุณต้องการ"พิมพ์ลงไป :)
slugster

6
@JoeBlow: ก่อนอื่นคุณยินดีมาก เกี่ยวกับ "ทำไมไม่" - ความคิดเห็นของคุณแสดงให้เห็นปัญหาอย่างชัดเจน เมื่อบางคนถามว่า "ทำไม" คำถามที่พวกเขากำลังมองหาเหตุผลเชิงตรรกะ บางคนกำลังมองหาเหตุผลในทางปฏิบัติ และคุณจะเห็นได้ชัดว่ามองหาเส้นของสเปคอธิบายกฎ มันคลุมเครือมากจนเป็นเรื่องยากมากที่จะสร้างคำตอบที่ดีซึ่งกำหนดเป้าหมายคำถามที่เป็นจริงในใจของผู้ถาม "ทำไมไม่" คำถามจะยิ่งแย่ลงเพราะพวกเขาเป็นคำถามที่คลุมเครือเกี่ยวกับสิ่งที่ไม่ได้อยู่
Eric Lippert

3
@EricLippert มันเลวร้ายยิ่งกว่าคำถามเกี่ยวกับสิ่งต่างๆที่ _might_ มีอยู่ : หากคุณกำลังทำงานกับซอฟต์แวร์กับทีมทั้งทีมใช้เวลาทุกวันตลอดทั้งปีในการคิดเกี่ยวกับคุณสมบัติและสร้างสมดุลให้กับผลที่ตามมา มีการตัดสินใจ คำขอคุณลักษณะและ "ทำไม" ขอเหตุผล อย่างไรก็ตามทำไมไม่บอกเป็นนัยว่าบางคนแค่ต้องการบางสิ่งบางอย่างซึ่งโดยพื้นฐานแล้วจะตั้งคำถามกับการตัดสินของทีมเอง ที่แย่กว่านั้นคือคนที่ถามว่าทำไมไม่ถามมักจะไม่ค่อยรู้เรื่อง ด้วยเหตุนี้ฉันจึงคิดว่าทำไมคำถามถึงไม่ยุติธรรม ดีมาก IMO
atlaste
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.