คุณควรประกาศวิธีการโดยใช้โอเวอร์โหลดหรือพารามิเตอร์เสริมใน C # 4.0 หรือไม่?


94

ฉันกำลังดูการพูดคุยของ Anders เกี่ยวกับ C # 4.0 และดูตัวอย่างของ C # 5.0และทำให้ฉันนึกถึงเมื่อพารามิเตอร์ที่เป็นทางเลือกมีอยู่ใน C # จะเป็นวิธีที่แนะนำในการประกาศวิธีการที่ไม่จำเป็นต้องระบุพารามิเตอร์ทั้งหมด

ยกตัวอย่างเช่นบางอย่างเช่นFileStreamชั้นจะมีประมาณสิบห้าก่อสร้างงานแตกต่างกันซึ่งสามารถแบ่งออกเป็น 'ครอบครัว' ตรรกะเช่นคนที่อยู่ด้านล่างจากสตริงคนจากและคนที่มาจากที่IntPtrSafeFileHandle

FileStream(string,FileMode);
FileStream(string,FileMode,FileAccess);
FileStream(string,FileMode,FileAccess,FileShare);
FileStream(string,FileMode,FileAccess,FileShare,int);
FileStream(string,FileMode,FileAccess,FileShare,int,bool);

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

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

คำตอบ:


123

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

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

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


22
+1 สำหรับภูมิปัญญาเกี่ยวกับลัทธิปฏิบัตินิยม: บางครั้งการตัดสินใจที่คุณไม่ชอบก็ง่ายกว่าการโต้แย้งกรณี
legends2k

13
@romkyns: ไม่เอฟเฟกต์ของการโอเวอร์โหลดไม่เหมือนกันกับจุดที่ 3 ด้วยการโอเวอร์โหลดที่ให้ค่าดีฟอลต์ค่าดีฟอลต์จะอยู่ในโค้ดไลบรารีดังนั้นหากคุณเปลี่ยนค่าเริ่มต้นและระบุไลบรารีเวอร์ชันใหม่ผู้โทรจะ ดูค่าเริ่มต้นใหม่โดยไม่ต้องคอมไพล์ใหม่ ในขณะที่มีพารามิเตอร์ที่เป็นทางเลือกคุณจะต้องคอมไพล์ใหม่เพื่อ "ดู" ค่าเริ่มต้นใหม่ หลายครั้งที่มันไม่ใช่ความแตกต่างที่สำคัญ แต่มันคือความแตกต่าง
Jon Skeet

สวัสดี @JonSkeet ฉันต้องการทราบว่าเราใช้ทั้งฟังก์ชัน ie กับพารามิเตอร์เสริมและอื่น ๆ ที่มีการโอเวอร์โหลดจะเรียกวิธีการใด ?? เช่น Add (int a, int b) และ Add (int a, int b, int c = 0) และ function call say: Add (5,10); วิธีใดที่จะเรียกว่าฟังก์ชันโอเวอร์โหลดหรือฟังก์ชันพารามิเตอร์เสริม ขอบคุณ :)
SHEKHAR SHETE

@Shekshar: คุณลองหรือยัง? อ่านข้อมูลจำเพาะเพื่อดูรายละเอียด แต่โดยพื้นฐานแล้วใน tie-breaker วิธีการที่คอมไพเลอร์ไม่ต้องกรอกพารามิเตอร์เสริมใด ๆ ชนะ
Jon Skeet

@JonSkeet ตอนนี้ฉันลองใช้ข้างต้น ... ฟังก์ชั่นโอเวอร์โหลดชนะพารามิเตอร์เสริม :)
SHEKHAR SHETE

19

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

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

ฉันใช้ตัวเลือกย้อนกลับใน VB6 วันของฉันและหลังจากนั้นพลาดมันจะลดความซ้ำซ้อนของความคิดเห็น XML จำนวนมากใน C #


11

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

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

และผมชอบความคิดที่ว่ามีทั่วไปหนึ่งซุปเปอร์วิธีการและส่วนที่เหลือเป็นห่อง่ายรอบที่หนึ่ง


1
ฉันเห็นด้วยอย่างมากกับสิ่งนี้อย่างไรก็ตามมีข้อแม้ว่าเมื่อคุณมีวิธีการที่รับพารามิเตอร์หลายตัว (3+) ซึ่งโดยธรรมชาติแล้วทั้งหมดเป็น "ทางเลือก" (สามารถแทนที่ด้วยค่าเริ่มต้น) คุณอาจต้องเรียงสับเปลี่ยนหลายรายการ ของลายเซ็นวิธีการเพื่อประโยชน์อื่นใด พิจารณาFoo(A, B, C)ต้องFoo(A), Foo(B), Foo(C), Foo(A, B), ,Foo(A, C) Foo(B, C)
Dan Lugg

7

ฉันจะใช้คุณสมบัติพารามิเตอร์เสริมของ 4.0 อย่างแน่นอน มันกำจัดไร้สาระ ...

public void M1( string foo, string bar )
{
   // do that thang
}

public void M1( string foo )
{
  M1( foo, "bar default" ); // I have always hated this line of code specifically
}

... และวางค่าที่ผู้โทรสามารถมองเห็นได้ ...

public void M1( string foo, string bar = "bar default" )
{
   // do that thang
}

ง่ายกว่ามากและเกิดข้อผิดพลาดน้อยกว่ามาก ฉันเคยเห็นสิ่งนี้เป็นข้อผิดพลาดในกรณีโอเวอร์โหลด ...

public void M1( string foo )
{
   M2( foo, "bar default" );  // oops!  I meant M1!
}

ฉันยังไม่ได้เล่นกับมาตรฐาน 4.0 แต่ฉันจะไม่ตกใจเมื่อรู้ว่าผู้ปฏิบัติตามเพียงแค่ปล่อยโอเวอร์โหลดให้คุณ


6

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

ผลลัพธ์ที่สำคัญของการเชื่อมโยงของพารามิเตอร์ทางเลือกที่ไซต์การเรียกใช้คือค่าเหล่านี้จะถูกกำหนดค่าตามเวอร์ชันของรหัสเป้าหมายซึ่งมีให้สำหรับคอมไพลเลอร์ หากแอสเซมบลีFooมีเมธอดที่Boo(int)มีค่าดีฟอลต์เป็น 5 และแอสเซมบลีBarมีการเรียกFoo.Boo()ใช้คอมไพลเลอร์จะประมวลผลเป็นไฟล์Foo.Boo(5). หากค่าเริ่มต้นเปลี่ยนเป็น 6 และFooคอมไพล์แอสเซมบลีใหม่Barจะยังคงเรียกใช้ต่อไปFoo.Boo(5)เว้นแต่หรือจนกว่าจะคอมไพล์ใหม่ด้วยเวอร์ชันใหม่ของFoo. ดังนั้นจึงควรหลีกเลี่ยงการใช้พารามิเตอร์ทางเลือกสำหรับสิ่งที่อาจเปลี่ยนแปลง


Re: "ดังนั้นควรหลีกเลี่ยงการใช้พารามิเตอร์เสริมสำหรับสิ่งที่อาจเปลี่ยนแปลง" ฉันยอมรับว่านี่อาจเป็นปัญหาได้หากรหัสไคลเอ็นต์ไม่มีใครสังเกตเห็นการเปลี่ยนแปลง void Foo(int value) … void Foo() { Foo(42); }อย่างไรก็ตามปัญหาเดียวกันอยู่เมื่อค่าเริ่มต้นจะถูกซ่อนอยู่ภายในวิธีการโอเวอร์โหลด: จากภายนอกผู้โทรไม่ทราบว่าค่าเริ่มต้นใดถูกใช้และอาจเปลี่ยนแปลงเมื่อใด เราจะต้องตรวจสอบเอกสารที่เป็นลายลักษณ์อักษรสำหรับสิ่งนั้น ค่าดีฟอลต์สำหรับพารามิเตอร์ที่เป็นทางเลือกสามารถมองเห็นได้ดังนี้: documentation-in-code ว่าค่าดีฟอลต์คืออะไร
stakx - ไม่ร่วมให้ข้อมูลใน

@stakx: หากโอเวอร์โหลดเชนแบบไม่มีพารามิเตอร์เป็นโอเวอร์โหลดด้วยพารามิเตอร์การเปลี่ยนค่า "ดีฟอลต์" ของพารามิเตอร์นั้นและการคอมไพล์นิยามของโอเวอร์โหลดซ้ำจะเปลี่ยนค่าที่ใช้แม้ว่าโค้ดการโทรจะไม่ได้คอมไพล์ซ้ำ
supercat

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

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

@stakx: ฉันบอกว่า "หลีกเลี่ยงการใช้" แทนที่จะ "ไม่ใช้" หากการเปลี่ยน X จะหมายความว่าการคอมไพล์ใหม่ครั้งต่อไปของ Y จะเปลี่ยนพฤติกรรมของ Y ซึ่งจะต้องมีการกำหนดค่าระบบบิลด์เพื่อให้การคอมไพล์ X ทุกครั้งจะทำการคอมไพล์ Y ใหม่ด้วย (ทำให้สิ่งต่างๆช้าลง) หรือสร้างความเสี่ยงที่โปรแกรมเมอร์จะเปลี่ยนแปลง X ในลักษณะที่จะทำลาย Y ในครั้งต่อไปที่มีการคอมไพล์และจะค้นพบความแตกดังกล่าวในเวลาต่อมาเมื่อ Y ถูกเปลี่ยนด้วยเหตุผลบางอย่างที่ไม่เกี่ยวข้องกันโดยสิ้นเชิง ควรใช้พารามิเตอร์เริ่มต้นเมื่อข้อดีของมันมีมากกว่าต้นทุนดังกล่าว
supercat

4

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

อาร์กิวเมนต์ที่เป็นทางเลือกเมื่อใช้ร่วมกับอาร์กิวเมนต์ที่ระบุชื่อจะมีประโยชน์อย่างมากเมื่อรวมกับการเรียก COM แบบ long-argument-list-with-all-optionals

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


3

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

public Rectangle (Point start = Point.Zero, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

แทนสิ่งนี้:

public Rectangle (Point start, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

public Rectangle (int width, int height) :
    this (Point.Zero, width, height)
{
}

เห็นได้ชัดว่าตัวอย่างนี้ง่ายมาก แต่ในกรณีของ OP ที่มีการโอเวอร์โหลด 5 ครั้งสิ่งต่างๆอาจแออัดได้อย่างรวดเร็ว


7
ฉันได้ยินมาว่าพารามิเตอร์ที่เป็นทางเลือกควรเป็นค่าสุดท้ายใช่ไหม
Ilya Ryzhenkov

ขึ้นอยู่กับการออกแบบของคุณ บางทีอาร์กิวเมนต์ "เริ่มต้น" มักจะมีความสำคัญยกเว้นเมื่อไม่เป็นเช่นนั้น บางทีคุณอาจมีลายเซ็นเดียวกันที่อื่นซึ่งมีความหมายแตกต่างออกไป สำหรับตัวอย่างที่สร้างขึ้นสี่เหลี่ยมผืนผ้าสาธารณะ (ความกว้าง int, ความสูง int, Point innerSquareStart, Point innerSquareEnd) {}
Robert P

13
จากสิ่งที่พวกเขาพูดในการพูดคุยพารามิเตอร์ทางเลือกต้องอยู่หลังพารามิเตอร์ที่ต้องการ
Greg Beech

3

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

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

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

แน่นอนว่านี่ไม่ใช่คำตอบที่จัดการทุกแง่มุม แต่ฉันคิดว่ามันเพิ่มอีกอย่างที่ยังไม่ครอบคลุม


1

แม้ว่าพวกเขาจะเป็น (ตามที่คาดการณ์ไว้?) สองวิธีที่เหมือนกันในเชิงแนวคิดที่พร้อมใช้งานสำหรับคุณในการสร้างแบบจำลอง API ของคุณตั้งแต่เริ่มต้น แต่น่าเสียดายที่มีความแตกต่างเล็กน้อยเมื่อคุณต้องพิจารณาความเข้ากันได้ของรันไทม์ย้อนหลังสำหรับไคลเอนต์เก่าของคุณ เพื่อนร่วมงานของฉัน (ขอบคุณเบรนท์!) ชี้ให้ฉันเห็นโพสต์ที่ยอดเยี่ยมนี้: ปัญหาการกำหนดเวอร์ชันที่มีข้อโต้แย้งเพิ่มเติม คำพูดบางส่วนจากมัน:

เหตุผลที่พารามิเตอร์ทางเลือกถูกนำมาใช้กับ C # 4 ในตอนแรกคือการสนับสนุน COM interop แค่นั้นแหละ. และตอนนี้เรากำลังเรียนรู้เกี่ยวกับผลกระทบทั้งหมดของข้อเท็จจริงนี้ หากคุณมีเมธอดที่มีพารามิเตอร์ที่เป็นทางเลือกคุณจะไม่สามารถเพิ่มโอเวอร์โหลดด้วยพารามิเตอร์ทางเลือกเพิ่มเติมได้เพราะกลัวว่าจะทำให้เวลาคอมไพล์เปลี่ยนแปลงหมด และคุณไม่สามารถลบโอเวอร์โหลดที่มีอยู่ได้เนื่องจากนี่เป็นการเปลี่ยนแปลงที่ทำลายรันไทม์มาโดยตลอด คุณต้องปฏิบัติเหมือนอินเทอร์เฟซ การขอความช่วยเหลือทางเดียวของคุณในกรณีนี้คือการเขียนวิธีการใหม่ด้วยชื่อใหม่ ดังนั้นโปรดระวังสิ่งนี้หากคุณวางแผนที่จะใช้อาร์กิวเมนต์ที่เป็นทางเลือกใน API ของคุณ


1

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

รหัสเริ่มต้น

public string HandleError(string message, bool silent=true, bool isCritical=true)
{
  ...
}

สมมติว่านี่เป็นหนึ่งในผู้โทรหลายวิธีข้างต้น:

HandleError("Disk is full", false);

ที่นี่เหตุการณ์ไม่เงียบและถือเป็นขั้นวิกฤต

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

หลังจาก refactor

การโทรในอดีตยังคงรวบรวมและสมมติว่ามันหลุดผ่าน refactor ไม่เปลี่ยนแปลง:

public string HandleError(string message, /*bool silent=true,*/ bool isCritical=true)
{
  ...
}

...

// Some other distant code file:
HandleError("Disk is full", false);

ตอนนี้falseจะมีเอฟเฟกต์ที่ไม่ได้ตั้งใจเหตุการณ์จะไม่ถือว่าสำคัญอีกต่อไป

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

โปรดทราบว่าปัญหาเดียวกันนี้มีหลายรูปแบบ รูปแบบหนึ่งที่อื่น ๆ ที่ระบุไว้ที่นี่

HandleError("Disk is full", silent:false)ยังทราบว่าอย่างเคร่งครัดโดยใช้พารามิเตอร์ชื่อเมื่อเรียกวิธีการที่จะหลีกเลี่ยงปัญหาเช่นนี้: อย่างไรก็ตามอาจเป็นไปไม่ได้ที่จะสมมติว่านักพัฒนาอื่น ๆ (หรือผู้ใช้ API สาธารณะ) จะทำเช่นนั้น

ด้วยเหตุผลเหล่านี้ฉันจะหลีกเลี่ยงการใช้พารามิเตอร์ทางเลือกใน API สาธารณะ (หรือแม้แต่วิธีสาธารณะหากอาจใช้กันอย่างแพร่หลาย) เว้นแต่จะมีข้อควรพิจารณาที่น่าสนใจอื่น ๆ


0

พารามิเตอร์ทางเลือกทั้งสองวิธีการโอเวอร์โหลดมีข้อดีหรือข้อเสียขึ้นอยู่กับความต้องการของคุณที่จะเลือกระหว่างพารามิเตอร์เหล่านี้

พารามิเตอร์ทางเลือก: พร้อมใช้งานใน. Net 4.0 เท่านั้น พารามิเตอร์ทางเลือกลดขนาดโค้ดของคุณ คุณไม่สามารถกำหนดและอ้างอิงพารามิเตอร์

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


0

ในหลายกรณีพารามิเตอร์ทางเลือกถูกใช้เพื่อสลับการดำเนินการ ตัวอย่างเช่น:

decimal GetPrice(string productName, decimal discountPercentage = 0)
{

    decimal basePrice = CalculateBasePrice(productName);

    if (discountPercentage > 0)
        return basePrice * (1 - discountPercentage / 100);
    else
        return basePrice;
}

พารามิเตอร์ Discount ที่นี่ใช้เพื่อป้อนคำสั่ง if-then-else มีความหลากหลายที่ไม่ได้รับการยอมรับจากนั้นจึงนำไปใช้เป็นคำสั่ง if-then-else ในกรณีเช่นนี้จะเป็นการดีกว่ามากที่จะแบ่งการควบคุมทั้งสองกระแสออกเป็นสองวิธีที่เป็นอิสระ:

decimal GetPrice(string productName)
{
    decimal basePrice = CalculateBasePrice(productName);
    return basePrice;
}

decimal GetPrice(string productName, decimal discountPercentage)
{

    if (discountPercentage <= 0)
        throw new ArgumentException();

    decimal basePrice = GetPrice(productName);

    decimal discountedPrice = basePrice * (1 - discountPercentage / 100);

    return discountedPrice;

}

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

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

สถานการณ์คล้ายกันมากกับการมีพารามิเตอร์ที่สามารถเป็นโมฆะได้ if (x == null)นั่นคือความคิดที่ดีอย่างเท่าเทียมกันเมื่อการดำเนินการเดือดงบเช่น

คุณสามารถดูการวิเคราะห์โดยละเอียดได้ที่ลิงก์เหล่านี้: การหลีกเลี่ยงพารามิเตอร์ที่เป็นทางเลือกและการหลีกเลี่ยงพารามิเตอร์ Null


0

หากต้องการเพิ่มเกมง่ายๆเมื่อใช้โอเวอร์โหลดแทนตัวเลือก:

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

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

ตัวอย่าง:

enum Match {
    Regex,
    Wildcard,
    ContainsString,
}

// Don't: This way, Enumerate() can be called in a way
//         which does not make sense:
IEnumerable<string> Enumerate(string searchPattern = null,
                              Match match = Match.Regex,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);

// Better: Provide only overloads which cannot be mis-used:
IEnumerable<string> Enumerate(SearchOption searchOption = SearchOption.TopDirectoryOnly);
IEnumerable<string> Enumerate(string searchPattern, Match match,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.