เหตุใดจึงต้องใช้คำหลักพารามิเตอร์


336

ฉันรู้ว่านี่เป็นคำถามพื้นฐาน แต่ฉันไม่พบคำตอบ

ทำไมต้องใช้ หากคุณเขียนฟังก์ชั่นหรือวิธีการที่ใช้มันเมื่อคุณลบมันรหัสจะยังคงทำงานได้อย่างสมบูรณ์ 100% โดยไม่ต้องใช้มัน เช่น:

ด้วย params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

ไม่มีพารามิเตอร์:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}

62
รหัสของวิธีการที่ตัวเองจะยังคงทำงานได้อย่างสมบูรณ์แบบที่ ... เรียกรหัสอาจจะดีไม่ได้ ...
จอนสกีต

7
คำสำคัญ params หมายถึงพารามิเตอร์ตัวเลือกที่สามารถส่งผ่านหรือไม่ไปยังวิธีการ อาร์เรย์ที่ไม่มีคำหลัก params หมายความว่าคุณต้องผ่านอาร์กิวเมนต์อาร์เรย์ไปยังเมธอด
Ailayna Entarria

งูหลามดำเนินภาษาแนวคิดเดียวกันเพื่อหวานมีเครื่องหมายดอกจัน ( *) พารามิเตอร์คำนำหน้าเป็นที่กล่าวถึงที่นี่
RBT

คำตอบ:


485

ด้วยparamsคุณสามารถโทรหาวิธีการของคุณเช่นนี้

addTwoEach(1, 2, 3, 4, 5);

หากไม่มีparamsคุณก็ไม่สามารถทำได้

นอกจากนี้คุณสามารถเรียกใช้เมธอดด้วยอาร์เรย์เป็นพารามิเตอร์ในทั้งสองกรณี :

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

นั่นคือparamsอนุญาตให้คุณใช้ทางลัดเมื่อเรียกใช้เมธอด

ไม่เกี่ยวข้องคุณสามารถทำให้วิธีการของคุณสั้นลงอย่างมาก:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}

17
@Ken: คุณอาจต้องนำเข้าSystem.Linqnamespace :)
Ranhiru Jude Cooray

49
หรือส่งคืน args.Sum (i => i + 2);
bornfromanegg

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

2
คุณสามารถใช้ return args.Select(x => x + 2).Sum();
bbvg

1
เมื่อคุณเพิ่มparamsคุณล็อคตัวเองจากการเพิ่มข้อโต้แย้งวิธีการเพิ่มเติมโดยไม่ทำลายผู้โทรหรือวิธีแก้ปัญหา
Mr. Young

93

การใช้paramsช่วยให้คุณสามารถเรียกใช้ฟังก์ชันโดยไม่มีข้อโต้แย้ง โดยไม่ต้องparams:

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

เปรียบเทียบกับparams:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

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


13
อันที่จริงแล้วอาเรย์จะว่างเปล่า new int[0]. หวังว่านี่จะช่วยได้! :)
vidstige

22

ช่วยให้คุณสามารถเพิ่มพารามิเตอร์ประเภทฐานได้มากเท่าที่คุณต้องการ

addTwoEach(10, 2, 4, 6)

ในขณะที่มีรูปแบบที่สองคุณต้องใช้อาร์เรย์เป็นพารามิเตอร์

addTwoEach(new int[] {10,2,4,6})

17

สิ่งหนึ่งที่อันตรายกับparamsKeyword คือถ้าหลังจากการโทรไปยังวิธีได้รับการเข้ารหัสแล้ว

  1. บางคนตั้งใจลบพารามิเตอร์ที่ต้องการอย่างน้อยหนึ่งรายการออกจากวิธีลายเซ็นและ
  2. พารามิเตอร์ที่ต้องการอย่างน้อยหนึ่งพารามิเตอร์ก่อนหน้าพารามิเตอร์ทันทีก่อนparamsการเปลี่ยนแปลงลายเซ็นคือประเภทที่เข้ากันได้กับparamsพารามิเตอร์

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

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

สำหรับฉันมันไม่คุ้มค่าทางลัด (Type)[]โดยไม่ต้องparamsทำงานกับ 0 ถึงพารามิเตอร์อนันต์ # ของโดยไม่ต้องแทนที่ กรณีที่เลวร้ายที่สุดคือคุณจะต้องเพิ่ม a , new (Type) [] {}ไปยังการโทรที่ไม่ได้ใช้

Btw, imho, ปลอดภัยที่สุด (และวิธีปฏิบัติที่อ่านได้มากที่สุด) คือ:

  1. ผ่าน Named Parameters (ซึ่งเราสามารถทำได้แม้ใน C # ~ 2 ทศวรรษหลังจากที่เราทำได้ใน VB; P) (เพราะ:

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

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

    1.3 หลีกเลี่ยงการนับคอมมาและข้ามไปและกลับจาก Call to Signature เพื่อดูว่า Expression ใดที่ถูกส่งผ่านสำหรับพารามิเตอร์ใดและ

    1.3.1 โดยวิธีการที่เหตุผลเดียวนี้ควรจะมีความอุดมสมบูรณ์ (ในแง่ของการหลีกเลี่ยงการละเมิดเกิดข้อผิดพลาดที่พบบ่อยของ DRY หลักการเพียงเพื่ออ่านรหัสไม่ต้องพูดถึงยังปรับเปลี่ยนมัน) แต่เหตุผลนี้สามารถชี้แจงความสำคัญมากขึ้นถ้ามีหนึ่ง / มีนิพจน์ที่ถูกส่งผ่านว่ามีเครื่องหมายจุลภาคเช่นการอ้างอิงอาร์เรย์หลายมิติหรือการเรียกใช้ฟังก์ชันพารามิเตอร์หลายพารามิเตอร์ ในกรณีดังกล่าวคุณไม่สามารถใช้งานได้ (ซึ่งแม้ว่าคุณจะทำได้ยังคงเพิ่มขั้นตอนพิเศษต่อพารามิเตอร์ต่อการเรียกใช้เมธอด) การค้นหาทั้งหมดที่เกิดขึ้นในคุณลักษณะการเลือกในโปรแกรมแก้ไขของคุณเพื่อทำการนับจุลภาคให้คุณโดยอัตโนมัติ

    1.4 หากคุณต้องใช้พารามิเตอร์เสริม ( paramsหรือไม่ก็ได้) จะช่วยให้คุณสามารถค้นหาการโทรที่มีการกำหนดพารามิเตอร์เสริมเฉพาะ (ดังนั้นจึงมีความเป็นไปได้ที่จะไม่ใช่ค่าเริ่มต้น)

(หมายเหตุ: เหตุผล 1.2. และ 1.3. สามารถทำให้ง่ายขึ้นและลดโอกาสเกิดข้อผิดพลาดแม้จะมีการเข้ารหัสการโทรเริ่มต้นไม่ต้องพูดถึงเมื่อต้องอ่านและ / หรือเปลี่ยนการโทร))

และ

  1. ทำหนึ่งพารามิเตอร์พารามิเตอร์ต่อบรรทัดเพื่อให้อ่านง่ายขึ้น (เพราะ:

    2.1 มันรกน้อยลงและ

    2.2 หลีกเลี่ยงการเลื่อนไปทางขวาและกลับซ้าย (และต้องทำเช่นนั้นต่อ - LINE เนื่องจากมนุษย์ส่วนใหญ่ไม่สามารถอ่านส่วนด้านซ้ายของหลายบรรทัดเลื่อนไปทางขวาและอ่านส่วนขวา))

    2.3 มันสอดคล้องกับ "วิธีปฏิบัติที่ดีที่สุด" ที่เราพัฒนาไปแล้วสำหรับคำสั่งการมอบหมายเนื่องจากทุกพารามิเตอร์ที่ส่งผ่านนั้นเป็นสาระสำคัญของคำสั่งการมอบหมาย (การกำหนดค่าหรือการอ้างอิงถึงตัวแปรท้องถิ่น) เช่นเดียวกับผู้ที่ปฏิบัติตาม"แนวทางปฏิบัติที่ดีที่สุด" ล่าสุดในรูปแบบการเข้ารหัสจะไม่ฝันที่จะเข้ารหัสคำสั่งการมอบหมายหลายรายการต่อบรรทัดเราอาจไม่ควร (และจะไม่เคย "การปฏิบัติที่ดีที่สุด" มาถึง "อัจฉริยะ" ของฉัน; ) ทำเช่นนั้นเมื่อผ่านพารามิเตอร์

หมายเหตุ :

  1. การส่งผ่านตัวแปรที่มีชื่อสะท้อนพารามิเตอร์ 'ไม่ได้ช่วยเมื่อ:

    1.1 คุณกำลังผ่านค่าคงที่ตัวอักษร (เช่น 0/1 ง่าย ๆ เท็จ / จริงหรือเป็นโมฆะที่แม้แต่ "'วิธีปฏิบัติที่ดีที่สุด'" อาจไม่ต้องการให้คุณใช้ค่าคงที่ที่มีชื่อสำหรับและวัตถุประสงค์ของพวกเขาไม่สามารถอนุมานได้ง่ายจากชื่อวิธี )

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

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

  2. การมีฟีเจอร์การตัดคำอัตโนมัติอย่าง VS นั้นทำได้เพียงกำจัดหนึ่ง (# 2.2) จาก 8 เหตุผลที่ฉันให้ไว้ข้างต้น ก่อนที่จะถึงปลายปี 2015 มันไม่ได้เยื้องอัตโนมัติ (!?! จริง ๆ MS?!?) ซึ่งเพิ่มความรุนแรงของเหตุผล # 2.1

VS ควรมีตัวเลือกที่สร้างตัวอย่างการเรียกใช้เมธอดด้วย Named Parameters (หนึ่งรายการต่อบรรทัด; P) และตัวเลือกคอมไพเลอร์ที่ต้องการ Named Parameters (คล้ายกับแนวคิดใน Option Explicit ใน VB ซึ่ง btw ความต้องการของ prolly เคยคิดไว้ เท่ากันอย่างร้ายกาจ แต่ตอนนี้ถูกเรียกร้องโดย "แนวทางปฏิบัติที่ดีที่สุด" ") ในความเป็นจริง "กลับมาในของฉันday ";) ในปี 1991 เพียงแค่เดือนเดียวกับอาชีพของฉันแม้กระทั่งก่อนที่ฉันจะใช้ (หรือเคยเห็น) ภาษาที่มี Named Parameters ฉันมี anti-sheeple /" เพียงแค่คุณสามารถทำได้ไม่ได้หมายความว่าคุณควรจะ " / อย่าสุ่มสี่สุ่มห้า "ตัดปลายของเนื้อย่าง" ให้รู้สึกพอที่จะจำลองมัน (โดยใช้ความเห็นแบบอินไลน์) โดยไม่ต้องเห็นใครทำเช่นนั้นโดยไม่ต้องใช้พารามิเตอร์ที่มีชื่อ (รวมถึงไวยากรณ์อื่น ๆ ที่ช่วยประหยัด "" ล้ำค่า "" รหัสแหล่งที่มาการกดแป้นพิมพ์) เป็นที่ระลึกของยุค Punch บัตรเมื่อส่วนใหญ่ของไวยากรณ์เหล่านี้เริ่มต้น. ไม่มีข้อแก้ตัวใด ๆ สำหรับการที่มีฮาร์ดแวร์ที่ทันสมัยและ IDE และซอฟต์แวร์ที่ซับซ้อนมากขึ้นที่สามารถอ่านได้เป็นอย่างมากเป็นมาก, มากสำคัญกว่า. "อ่านรหัสบ่อยกว่าที่เขียน" ตราบใดที่คุณไม่ได้ทำสำเนารหัสที่ไม่ได้รับการปรับปรุงอัตโนมัติทุกครั้งที่บันทึกการกดแป้นพิมพ์มีแนวโน้มที่จะมีค่าใช้จ่ายมากขึ้นเมื่อมีคน (แม้แต่ตัวคุณเอง) พยายามอ่านในภายหลัง


2
ฉันไม่เข้าใจ ทำไมคุณไม่บังคับใช้อย่างน้อยก็มีอย่างน้อยหนึ่งตัว แม้ไม่มี params ก็ไม่มีอะไรที่จะห้ามไม่ให้คุณผ่านnullหรือnew object[0]โต้แย้ง
Casey

อาจเป็นอันตรายเกินไปที่จะมีพารามิเตอร์ที่จำเป็นก่อนหน้าตัวเลือก (ในกรณีที่หนึ่งหรือมากกว่าของพารามิเตอร์ที่ต้องการจะถูกลบออกหลังจากการโทรถูกเข้ารหัส) นั่นอาจเป็นสาเหตุที่ฉันไม่เคยเห็นพารามิเตอร์ที่ต้องการมาก่อนพารามิเตอร์ตัวเลือกในโค้ดตัวอย่างในเอกสารใด ๆ เกี่ยวกับพารามิเตอร์ทางเลือก Btw, imho, ปลอดภัยที่สุด (และวิธีปฏิบัติที่อ่านได้มากที่สุด) คือการส่งผ่านพารามิเตอร์ที่มีชื่อ (ซึ่งตอนนี้เราสามารถทำได้แม้ใน C # ~ 2 ทศวรรษหลังจากที่เราทำได้ใน VB) VS ควรมีตัวเลือกที่สร้างตัวอย่างการเรียกใช้เมธอดพร้อมพารามิเตอร์ที่กำหนดชื่อ (และทำเช่นนั้น 1 พารามิเตอร์ต่อบรรทัด)
Tom

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

1
อดีต ผมประกาศเป็นmyMethod void myMethod(int requiredInt, params int[] optionalInts)I / คนอื่นรหัสหนึ่ง / โทรมากขึ้นเช่นmyMethod(1), ,myMethod(1, 21) myMethod(1, 21, 22)ฉันจะเปลี่ยนให้เป็นmyMethod void myMethod(params int[] optionalInts)การเรียกเหล่านั้นทั้งหมดจะยังคงรวบรวมโดยไม่มีข้อผิดพลาดแม้ว่าพารามิเตอร์ที่ 1 ของพวกเขา ("1") นั้นไม่ได้มีเจตนาชัดเจนที่จะส่งผ่านเป็นองค์ประกอบที่ 1 ของoptionalIntsพารามิเตอร์
Tom

โอ้ โอเคในกรณีนั้นมันอาจไม่เหมาะสม ฉันไม่คิดว่ามีเหตุผลใดที่จะหลีกเลี่ยงได้หากคุณต้องการสตริงและ 0 ต่อจำนวนมากหรืออะไรก็ตาม
Casey

10

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

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);

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

คุณอยู่ในระดับหนึ่ง แต่สิ่งที่ทำให้มันเจ๋งคือโอเวอร์โหลดที่ไม่มีพารามิเตอร์อินพุตเช่น int sum1 = addTwoEach ();
electricbah

5

params ยังอนุญาตให้คุณเรียกใช้เมธอดด้วยอาร์กิวเมนต์เดียว

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

คือแทนFoo(1); Foo(new int[] { 1 });มีประโยชน์สำหรับการจดชวเลขในสถานการณ์ที่คุณอาจต้องผ่านค่าเดียวมากกว่าทั้งอาร์เรย์ มันยังคงได้รับการจัดการในลักษณะเดียวกันในวิธีการ แต่ให้ขนมสำหรับเรียกวิธีนี้


5

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

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

เมื่อคุณจะเรียกวิธีการข้างต้นคุณสามารถเรียกวิธีดังต่อไปนี้:

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

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


0

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

public void ExampleMethod(params string[] args)
{
// do some stuff
}

โทร:

ExampleMethod();

จากนั้น. Net Framework เวอร์ชันใหม่จะทำสิ่งนี้ (จาก. Net Framework 4.6):

ExampleMethod(Array.Empty<string>());

Array.Emptyวัตถุนี้สามารถนำกลับมาใช้ใหม่โดยกรอบงานในภายหลังดังนั้นจึงไม่จำเป็นต้องทำการจัดสรรซ้ำซ้อน การจัดสรรเหล่านี้จะเกิดขึ้นเมื่อคุณเรียกวิธีนี้ดังนี้:

 ExampleMethod(new string[] {});

0

อาจฟังดูโง่ แต่ Params ไม่อนุญาตให้ใช้อาเรย์หลายมิติ โดยที่คุณสามารถส่งผ่านอาร์เรย์หลายมิติไปยังฟังก์ชันได้


0

ตัวอย่างอื่น

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

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