ทำไมพารามิเตอร์ทางเลือก C # 4 ที่กำหนดไว้ในส่วนต่อประสานไม่ได้ถูกบังคับใช้ในการใช้งานคลาส?


361

ฉันสังเกตเห็นว่าด้วยพารามิเตอร์ที่เป็นตัวเลือกใน C # 4 หากคุณระบุพารามิเตอร์ที่เป็นตัวเลือกบนอินเทอร์เฟซที่คุณไม่ต้องทำให้พารามิเตอร์นั้นเป็นตัวเลือกในชั้นเรียนการใช้งานใด ๆ :

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

และดังนั้นจึง:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

ไม่มีใครรู้ว่าทำไมพารามิเตอร์ที่ไม่จำเป็นได้รับการออกแบบมาเพื่อทำงานในลักษณะนี้?

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

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


22
เพราะพวกเขาเป็นตัวเลือก?
Oded

1
แต่คุณสามารถโยนวัตถุเช่นไปและเรียกว่ามีพารามิเตอร์ตัวเลือก:MyInterface ((MyInterface)obj).TestMethod();
Jim Mischel

7
@ รหัส - แต่ถ้าคุณบอกว่าพารามิเตอร์นี้เป็นทางเลือกในสัญญาทำไมคุณถึงอนุญาตให้ผู้ดำเนินการไม่ทำให้มันเป็นตัวเลือก? ไม่เพียงสร้างความสับสนให้กับทุกคนที่ต้องการใช้สัญญาหรือไม่
theburningmonk

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

2
คำอธิบายปัญหาโดยละเอียดเพิ่มเติมได้ที่นี่ -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

คำตอบ:


235

ปรับปรุง: คำถามนี้เป็นเรื่องของบล็อกของฉันเมื่อวันที่ 12 พฤษภาคม 2011 ขอบคุณสำหรับคำถามที่ยอดเยี่ยม!

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

สมมติว่าเราทำอย่างนั้น ตอนนี้สมมติว่านักพัฒนาไม่มีซอร์สโค้ดสำหรับการใช้งาน


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

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

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

บางทีในกรณีนั้นพวกเขาจะต้องพูดว่า:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

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


ปรับปรุง: ในความคิดเห็นด้านล่าง supercat แนะนำคุณสมบัติภาษาที่จะเพิ่มพลังให้กับภาษาอย่างแท้จริงและเปิดใช้งานบางสถานการณ์คล้ายกับที่อธิบายไว้ในคำถามนี้ FYI คุณลักษณะนั้น - การใช้งานวิธีการเริ่มต้นในอินเตอร์เฟส - จะถูกเพิ่มไปยัง C # 8


9
@supercat: เราไม่มีแผนจะทำเช่นนั้น แต่คุณสมบัติเช่นนั้นเป็นไปได้ มันถูกเสนอมาก่อน ในระหว่างกระบวนการออกแบบสำหรับ C # 4 เราเตะรอบสิ่งที่เราเรียกว่าคุณสมบัติ "ส่วนขยายทุกอย่าง" - เรามีวิธีการขยายดังนั้นมันหมายถึงอะไรที่จะมีเหตุการณ์ส่วนขยายคุณสมบัติส่วนขยายตัวสร้างส่วนขยายส่วนต่อขยายส่วนต่อขยาย . คลาสที่คุณสามารถ "แนบ" กับอินเทอร์เฟซและพูดว่า "วิธีการเหล่านี้เป็นการใช้งานเริ่มต้นของอินเทอร์เฟซนี้" เป็นวิธีหนึ่งที่เป็นไปได้ในการกำหนดลักษณะ "ส่วนต่อขยาย" ความคิดที่น่าสนใจ
Eric Lippert

16
เพื่อชี้แจงเหตุผลของฉันสำหรับเหตุผลที่ฉันคิดว่ามันไม่ง่าย: สำหรับฉันพารามิเตอร์ตัวเลือกคือ "ตัวเลือกสำหรับผู้เรียกของวิธีการ" ไม่ "เป็นทางเลือกสำหรับตัวดำเนินการของอินเทอร์เฟซ"
Stefan de Kok

8
"พวกเขาจำเป็นต้องใช้ในโลกของคุณเพื่อเรียกผู้แต่ง B ทางโทรศัพท์และขอให้พวกเขาส่งเวอร์ชั่นใหม่ [... ] ไปให้พวกเขาหรือไม่" ไม่ไม่ต้องใช้โทรศัพท์ B ไม่ควรถูกมองว่าเป็นการฝังตัวของ MyInterface และผู้เขียน D สามารถทำให้ทราบได้โดยคอมไพเลอร์ ผู้เขียน D จะต้องใช้ D กับ TestMethod ที่ยอมรับพารามิเตอร์เริ่มต้นเนื่องจากเป็นสิ่งที่ผู้สร้างส่วนต่อประสานต้องการ - คุณกำลังโต้แย้งว่าการอนุญาตให้ผู้เขียนส่วนต่อประสานบังคับใช้ข้อ จำกัด เพราะบางคนอาจต้องการทำลายมัน นั่นไม่ใช่ข้อโต้แย้งที่ดี
philofinfinitejest

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

8
ฉันเห็นด้วยกับ @philofinfinitejest ที่นี่ อินเทอร์เฟซบอกสิ่งที่สามารถทำได้ หากฉันผ่านการใช้งานอินเทอร์เฟซที่มีค่าเริ่มต้นต่างไปจากที่อินเตอร์เฟสกำหนดไว้ฉันจะรู้ได้อย่างไร เฮ้ฉันมีอินเทอร์เฟซที่ส่งผ่านค่าเริ่มต้นจริงดังนั้นทำไมสิ่งนี้ถึงเป็นเท็จ ดูเหมือนว่าฉันจะไม่ได้รับอินเทอร์เฟซที่ฉันคาดไว้ ที่แย่กว่านั้นคือตอนนี้ฉันต้องตั้งโปรแกรมให้กับการติดตั้งใช้งานไม่ใช่อินเตอร์เฟส
aaronburro

47

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

การโทรobj2.TestMethod();จะถูกแทนที่ด้วยobj2.TestMethod(false);เมื่อรหัส C # ได้รับการรวบรวมเป็น IL ไม่ใช่เวลา JIT

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

ในทางกลับกันการตัดการเชื่อมต่อนี้หมายความว่าคุณไม่สามารถใช้คลาสคอนกรีตและอินเทอร์เฟซแทนกันได้เสมอ

แล้วคุณไม่สามารถทำว่าถ้าวิธีการอินเตอร์เฟซที่ได้รับการดำเนินการอย่างชัดเจน


1
คุณสามารถบังคับผู้ใช้งานให้ดำเนินการอย่างชัดเจนได้หรือไม่?
บดขยี้

30

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


7

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

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