วิธี c # กับ params ไม่ จำกัด หรือวิธีการที่มีอาร์เรย์หรือรายการ?


21

ฉันเพิ่งเรียนรู้ว่าคุณสามารถสร้างวิธีที่มีพารามิเตอร์ไม่ จำกัด เช่น:

SomeMethod(params int[] numbers);

แต่คำถามของฉันคืออะไรความแตกต่างระหว่างที่และเพียงแค่สร้างวิธีการที่ได้รับรายชื่อหรืออาร์เรย์?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

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

การค้นหาด่วนบน google ไม่ได้ช่วยฉันหวังว่าคุณจะช่วยฉันได้


ดูที่นี่: stackoverflow.com/questions/434761/…
Mr.AF

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

1
@digitlworld: หากหัวข้อนี้คุณสนใจโปรดดูgithub.com/dotnet/csharplang/issues/179เพื่อการสนทนา
Eric Lippert

ดูที่ลายเซ็นและ / Console.Writeหรือการดำเนินงานของ
3

คำตอบ:


27

ความแตกต่างระหว่างสิ่งนั้นและเพียงแค่สร้างวิธีการที่ได้รับรายการหรืออาร์เรย์คืออะไร

ความแตกต่างระหว่าง

void M(params int[] x)

และ

void N(int[] x)

คือว่า M อาจถูกเรียกเช่นนี้:

M(1, 2, 3)

หรือเช่นนี้

M(new int[] { 1, 2, 3 });

แต่ N อาจถูกเรียกในวิธีที่สองเท่านั้นไม่ใช่วิธีแรก

บางทีมันอาจมีผลกระทบต่อประสิทธิภาพบ้างไหม?

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

ฉันไม่เข้าใจหรือเห็นอย่างถ่องแท้ว่าคุณต้องการให้มีพารามิเตอร์แบบไม่ จำกัด จำนวนเท่าใด

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

M(1, 2, 3);

แทนที่จะเขียน

M(new int[] { 1, 2, 3 });

มันช่วยประหยัดการกดปุ่มเพียงไม่กี่ครั้งที่ฝั่งผู้โทร นั้นคือทั้งหมด.

คำถามสองสามข้อที่คุณไม่ได้ถาม แต่อาจต้องการทราบคำตอบ:

คุณสมบัตินี้เรียกว่าอะไร?

วิธีการที่ช่วยให้จำนวนตัวแปรของการขัดแย้งที่จะผ่านในด้านผู้โทรจะเรียกว่าvariadic วิธีการพารามิเตอร์เป็นวิธีการที่ C # ใช้วิธีการแปรปรวน

การแก้ไขการโอเวอร์โหลดทำงานอย่างไรกับวิธีการแปรปรวน?

เมื่อต้องเผชิญกับปัญหาการแก้ปัญหาการโอเวอร์โหลด C # จะพิจารณาทั้งแบบฟอร์ม "ปกติ" และ "ขยาย" และรูปแบบ "ปกติ" จะชนะถ้าทั้งคู่ใช้ได้ ตัวอย่างเช่นพิจารณาสิ่งนี้:

void P(params object[] x){}

และเรามีสาย

P(null);

มีสองความเป็นไปได้ที่เกี่ยวข้อง ในรูปแบบ "ปกติ" เราเรียกPและผ่านการอ้างอิงเป็นโมฆะสำหรับอาร์เรย์ ใน "ขยาย" P(new object[] { null })รูปแบบที่เราเรียกว่า ในกรณีนี้แบบฟอร์มปกติชนะ หากเรามีการโทรP(null, null)แบบฟอร์มปกติจะไม่สามารถใช้ได้และแบบฟอร์มที่ชนะจะขยายไปตามค่าเริ่มต้น

ท้าทาย : สมมติว่าเรามีและโทรvar s = new[] { "hello" }; P(s);อธิบายสิ่งที่เกิดขึ้นที่ไซต์การโทรและสาเหตุ คุณอาจประหลาดใจ!

ท้าทาย : สมมติว่าเรามีทั้งสองและvoid P(object x){} void P(params object[] x){}สิ่งที่ไม่P(null)ทำและทำไม?

ท้าทาย : สมมติว่าเรามีทั้งสองและvoid M(string x){} void M(params string[] x){}สิ่งที่ไม่M(null)ทำและทำไม? สิ่งนี้แตกต่างจากกรณีก่อนหน้าอย่างไร


Ch2: P(null)vs. P((object)null)vs. P((object[])null)- ฉันจะหาคำอธิบายเกี่ยวกับความแตกต่างนี้ได้ที่ไหน มันให้ความรู้สึกเหมือนnullมีบางชนิดพิเศษซึ่งจะแปลงไปยังอาร์เรย์มากกว่าวัตถุ ( P(null)) แต่พบการแปลงสตริงหรืออาร์เรย์ของสตริงคลุมเครือ ( M(null)) ... ที่รู้สึกแปลกมากที่จะคาดหวังว่ามันจะเป็นได้ทั้งที่ไม่ชัดเจนในทั้งสองกรณีหรือเลือก เวอร์ชัน single-arg (ซึ่งไม่ได้เป็น) แต่ฉันคิดว่ามันเกี่ยวกับparamsการเป็นเรื่องทั่วไปเช่นการอ้างอิงสากล ( &&) ในเทมเพลต C ++ และการจับคู่ที่ดีกว่า (ใน Ch2 ไม่ใช่ใน Ch3)
firda

@firda: คุณสามารถหาคำอธิบายได้ในข้อกำหนด C # เหตุผลที่ทำให้เกิดความแปลกคือ: null สามารถแปลงเป็นวัตถุสตริงวัตถุ [] และสตริง [] ดังนั้นคำถามที่โพสต์ถึงความละเอียดเกินพิกัดคือ: การแปลงใดดีที่สุด ? กฎการควบคุมในกรณีนี้คือเฉพาะจะดีกว่าทั่วไป เราจะบอกประเภทที่เฉพาะเจาะจงมากขึ้นได้อย่างไร กฎคือยีราฟทั้งหมดเป็นสัตว์ แต่ไม่ใช่สัตว์ทั้งหมดเป็นยีราฟดังนั้นยีราฟจึงมีความเฉพาะเจาะจงมากกว่าสัตว์ ทั้งหมดobject[]มีobjectแต่ไม่ทั้งหมดobjectมีobject[]จึงobject[]เป็นเฉพาะเจาะจงมากขึ้น
Eric Lippert

@firda: ตอนนี้คุณรู้ไหมว่าทำไมสายอักขระไม่ชัดเจน มันไม่เป็นความจริงเลยที่ "ทั้งหมดstring[]เป็นstring" ในความเป็นจริงไม่string[]เป็นstringและไม่stringเป็นstring[]ดังนั้นจึงstringไม่เฉพาะเจาะจงกว่าหรือทั่วไปกว่าstring[]และเราได้รับข้อผิดพลาดที่คลุมเครือเมื่อถูกขอให้เลือก
Eric Lippert

ทั้งหมดobject[]คือobject... object[]เฉพาะเจาะจงมากขึ้นดังนั้นP(null)ไปสำหรับอาร์เรย์เฉพาะเพิ่มเติมขอบคุณนั่นคือสิ่งที่ฉันหายไป พบกฎบางอย่างที่นี่: docs.microsoft.com/en-us/dotnet/csharp/language-reference/ …
firda

5

เพิ่งทำต้นแบบเล็ก ๆ คำตอบดูเหมือนว่าparamsเป็นเพียงน้ำตาลประโยคสำหรับการส่งผ่านในอาร์เรย์ นั่นไม่น่าแปลกใจจริงๆ ฉันสร้างวิธีการเดียวกันสองรุ่นโดยที่ความแตกต่างเพียงอย่างเดียวคือคำหลัก "params" IL ที่สร้างขึ้นสำหรับทั้งคู่นั้นเหมือนกันยกเว้นที่System.ParamArrayAttributeถูกนำไปใช้กับparamsเวอร์ชัน

นอกจากนี้ IL ที่สร้างขึ้นที่ไซต์การโทรก็เหมือนกันระหว่างฉันเรียกวิธีด้วยการประกาศด้วยตนเองnew int[]และเรียกใช้เมธอดเพียงแค่ใช้paramsอาร์กิวเมนต์

ดังนั้นคำตอบน่าจะเป็น "ความสะดวกสบาย" ดูเหมือนจะไม่มีความแตกต่างในประสิทธิภาพ คุณสามารถเรียกใช้paramsฟังก์ชันที่มีอาร์เรย์แทนได้ดังนั้นจึงไม่น่าประหลาดใจอย่างมาก มันลงมาถ้าผู้บริโภคของวิธีการของคุณเรียกมันด้วยพารามิเตอร์จำนวนเท่าใดก็ได้ (เช่นsomeMethod(1, 2, 3)) ง่ายกว่าการสร้างคอลเลกชันก่อนเสมอ (เช่นsomeMethod(new List<int>() { 1, 2, 3 } ))


4

คุณสมบัติของพารามิเตอร์ไม่ จำกัด ให้ประโยชน์ดังต่อไปนี้ในหลาย ๆ สถานการณ์:

  1. คลัปหลวม
  2. ปรับปรุงการใช้ซ้ำ
  3. ประสิทธิภาพโดยรวมที่ดีขึ้นของแอปพลิเคชัน

นี่คือตัวอย่างที่ตัวเลือกพารามิเตอร์ไม่ จำกัด เป็นตัวเลือกที่ดี

พิจารณาว่าจะต้องสร้างแอปพลิเคชันสำหรับส่งอีเมล

ฟังก์ชันที่ส่งอีเมลจะต้องสามารถจัดการค่าเดียวหรือหลายค่าสำหรับฟิลด์ 'ถึง', 'CC' และ 'BCC'

หากประเภทของพารามิเตอร์ได้รับการแก้ไขให้เป็นอาร์เรย์หรือรายการสำหรับฟิลด์ทั้งหมด (ถึง, CC, BCC) ดังนั้นฟังก์ชันการเรียกจะถูกบังคับให้จัดการกับความซับซ้อนทั้งหมดของการกำหนด 3 อาร์เรย์หรือรายการเพื่อเรียกใช้ฟังก์ชันผู้ส่งอีเมล .

แม้ว่าผู้โทรต้องการส่งอีเมลไปยังที่อยู่เพียงแห่งเดียวฟังก์ชั่นผู้ส่งอีเมลจะบังคับให้ผู้โทรกำหนดและส่ง 3 อาร์เรย์ที่แตกต่างกันเป็นพารามิเตอร์

หากฟังก์ชันผู้ส่งอีเมลใช้วิธี params แบบไม่ จำกัด ดังนั้นฟังก์ชันผู้โทรไม่จำเป็นต้องจัดการกับความซับซ้อนทั้งหมด

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


2

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

โดยส่วนตัวฉันจะใช้paramsเมื่อรหัสของฉันเป็นอย่างไร

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

ส่วนที่ดีเกี่ยวกับเรื่องนี้คือฉันสามารถใช้วิธีนี้ได้ทุกที่โดยไม่ต้องสร้างอาร์เรย์หรือรายการทุกครั้ง

ฉันจะใช้arrayหรือlistเมื่อฉันรู้ว่าฉันจะผ่านฟังก์ชั่นนี้เสมอชุดของข้อมูลที่รวบรวมไว้แล้วเช่น

List<User> users = GetUsersFromDB();
SomeMethod(users);

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


1

แบบแผนการโทรแตกต่างกัน ตัวอย่างเช่น ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

ในกรณีแรกคุณสามารถส่ง ints ได้มากเท่าพารามิเตอร์ที่แยกจากกันไปยังเมธอด ในวินาทีคุณจะต้องยกตัวอย่างรายการและส่งมัน

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