Tại sao các tham số tùy chọn C # 4 được định nghĩa trên giao diện không được thi hành khi triển khai lớp?


361

Tôi nhận thấy rằng với các tham số tùy chọn trong C # 4 nếu bạn chỉ định một tham số tùy chọn trên giao diện bạn không, bạn phải đặt tham số đó tùy chọn trên bất kỳ lớp triển khai nào:

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

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

và do đó:

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

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

Có ai biết tại sao các tham số tùy chọn được thiết kế để làm việc theo cách này?

Một mặt, tôi cho rằng khả năng ghi đè bất kỳ giá trị mặc định nào được chỉ định trên các giao diện là hữu ích mặc dù thành thật mà nói tôi không chắc liệu bạn có thể chỉ định các giá trị mặc định trên giao diện hay không vì đó là một quyết định triển khai.

Mặt khác, việc ngắt kết nối này có nghĩa là bạn không thể luôn sử dụng lớp cụ thể và giao diện thay thế cho nhau. Tất nhiên, điều này sẽ không thành vấn đề nếu giá trị mặc định được chỉ định khi triển khai, nhưng sau đó nếu bạn hiển thị lớp cụ thể của mình dưới dạng giao diện (ví dụ sử dụng một khung IOC để tiêm lớp cụ thể) thì thực sự không có điểm có giá trị mặc định là người gọi sẽ phải luôn luôn cung cấp nó.


22
Bởi vì chúng là tùy chọn?
Oded

1
Nhưng bạn có thể truyền đối tượng đến MyInterfacevà gọi nó với tham số tùy chọn : ((MyInterface)obj).TestMethod();.
Jim Mischel

7
@oding - nhưng nếu bạn nói tham số này là tùy chọn trong hợp đồng, tại sao bạn cho phép người thực hiện không làm cho nó tùy chọn? Không phải điều đó chỉ gây nhầm lẫn cho bất cứ ai muốn sử dụng hợp đồng?
theburningmonk

1
Tôi nghĩ trong trường hợp này bạn có thể nói rằng tham số này là tùy chọn trong việc triển khai, không phải là gọi các phương thức triển khai. Khi bạn gọi phương thức trong lớp, bạn phải tuân theo các quy tắc của lớp (tham số không phải là tùy chọn trong lớp để bạn có thể 'Gọi phương thức mà không có nó) và trong khi sử dụng giao diện, bạn phải tuân theo các quy tắc giao diện, do đó bạn có thể ghi đè các phương thức có / không có tham số tùy chọn. Chỉ là một ý kiến.
Mohamad Alhamoud

2
Chi tiết giải thích vấn đề tại đây -> geekswithbloss.net/BlackRmusCoder/archive/2010/06/17/ mẹo
Andrew Orsich

Câu trả lời:


236

CẬP NHẬT: Câu hỏi này là chủ đề của blog của tôi vào ngày 12 tháng 5 năm 2011. Cảm ơn câu hỏi tuyệt vời!

Giả sử bạn có một giao diện như bạn mô tả và một trăm lớp thực hiện nó. Sau đó, bạn quyết định đặt một trong các tham số của một trong các phương thức của giao diện. Bạn có gợi ý rằng điều đúng đắn cần làm là cho trình biên dịch buộc nhà phát triển tìm mọi cách thực hiện phương thức giao diện đó và làm cho tham số tùy chọn không?

Giả sử chúng ta đã làm điều đó. Bây giờ giả sử nhà phát triển không có mã nguồn để thực hiện:


// 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

Làm thế nào là tác giả của D phải làm cho tác phẩm này? Họ có yêu cầu trong thế giới của bạn gọi cho tác giả của B trên điện thoại và yêu cầu họ vui lòng gửi cho họ phiên bản B mới khiến phương thức có tham số tùy chọn không?

Điều đó sẽ không bay. Điều gì sẽ xảy ra nếu hai người gọi tác giả của B và một trong số họ muốn mặc định là đúng và một trong số họ muốn nó là sai? Điều gì xảy ra nếu tác giả của B chỉ đơn giản là từ chối chơi cùng?

Có lẽ trong trường hợp đó họ sẽ được yêu cầu nói:

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

Tính năng được đề xuất dường như thêm rất nhiều bất tiện cho lập trình viên mà không tăng sức mạnh đại diện tương ứng. Lợi ích hấp dẫn của tính năng này là gì để chứng minh chi phí gia tăng cho người dùng?


CẬP NHẬT: Trong các bình luận bên dưới, supercat gợi ý một tính năng ngôn ngữ thực sự sẽ thêm sức mạnh cho ngôn ngữ và cho phép một số tình huống tương tự như kịch bản được mô tả trong câu hỏi này. FYI, tính năng đó - triển khai mặc định các phương thức trong giao diện - sẽ được thêm vào C # 8.


9
@supercat: Chúng tôi không có bất kỳ kế hoạch nào để làm như vậy, nhưng một tính năng như thế là có thể. Nó đã được đề xuất trước đây. Trong quá trình thiết kế cho C # 4, chúng tôi đã giới thiệu rất nhiều tính năng mà chúng tôi gọi là tính năng "mở rộng mọi thứ" - chúng tôi có các phương thức mở rộng, vậy có nghĩa là gì khi có các sự kiện mở rộng, thuộc tính mở rộng, trình tạo tiện ích mở rộng, giao diện mở rộng, v.v. . Các lớp mà bạn có thể "đính kèm" vào các giao diện và nói "các phương thức này là các cài đặt mặc định của giao diện này" là một cách có thể để mô tả "giao diện mở rộng". Một ý tưởng thú vị.
Eric Lippert

16
để làm rõ lý do của tôi về lý do tại sao tôi nghĩ rằng nó không trực quan: với tôi một tham số tùy chọn là "tùy chọn cho người gọi phương thức" KHÔNG "tùy chọn cho người thực hiện giao diện".
Stefan de Kok

8
"Họ có yêu cầu trong thế giới của bạn gọi cho tác giả của B trên điện thoại và yêu cầu họ vui lòng gửi cho họ phiên bản mới [...] không?" Không, không cần gọi điện thoại. B đơn giản là không còn có thể được coi là một hàm ý của MyInterface và tác giả của D có thể nhận ra điều đó bởi trình biên dịch. Tác giả của D sẽ được yêu cầu triển khai D với TestMethod chấp nhận tham số mặc định vì đó là điều mà tác giả Giao diện yêu cầu - bạn đang tranh cãi về việc cho phép tác giả giao diện thực thi một ràng buộc vì ai đó có thể muốn phá vỡ nó. Đó không phải là một cuộc tranh luận tốt.
philofinfinitejest

7
@EricLippert Trong trường hợp tham số tùy chọn được chỉ định để thuận tiện cho việc mã hóa, vâng, việc thực thi sự bất tiện trong việc triển khai là phản trực giác. Nhưng trong trường hợp tham số tùy chọn đang được sử dụng để giảm thiểu quá tải phương thức, một tính năng mạnh mẽ, các giá trị tùy chọn đó có thể có ý nghĩa lớn và bỏ qua chúng chắc chắn làm giảm sức mạnh đó. Không đề cập đến trường hợp triển khai cụ thể chỉ định một giá trị mặc định khác với giao diện. Nó chỉ có vẻ như một ngắt kết nối rất kỳ lạ để cho phép.
philofinfinitejest

8
Tôi đồng ý với @philofinfinitejest tại đây. Một giao diện cho biết những gì có thể làm. Nếu tôi được thông qua triển khai giao diện có giá trị mặc định khác với giao diện chỉ định, làm sao tôi biết điều đó? Này, tôi có một giao diện vượt qua đúng như một mặc định, vậy tại sao thứ này lại bị sai? Điều đó, có vẻ như tôi KHÔNG nhận được giao diện mà tôi mong đợi. Thậm chí tệ hơn, bây giờ tôi phải lập trình để thực hiện chứ không phải giao diện.
aaronburro

47

Một tham số tùy chọn chỉ được gắn thẻ với một thuộc tính. Thuộc tính này báo cho trình biên dịch chèn giá trị mặc định cho tham số đó tại trang gọi.

Cuộc gọi obj2.TestMethod();được thay thế bằng obj2.TestMethod(false);khi mã C # được biên dịch sang IL chứ không phải tại thời điểm JIT.

Vì vậy, theo cách nào đó, người gọi luôn cung cấp giá trị mặc định với các tham số tùy chọn. Điều này cũng có hậu quả đối với phiên bản nhị phân: Nếu bạn thay đổi giá trị mặc định nhưng không biên dịch lại mã cuộc gọi, nó sẽ tiếp tục sử dụng giá trị mặc định cũ.

Mặt khác, việc ngắt kết nối này có nghĩa là bạn không thể luôn sử dụng lớp cụ thể và giao diện thay thế cho nhau.

Bạn đã không thể làm điều đó nếu phương thức giao diện được triển khai rõ ràng .


1
Bạn có thể buộc người thực hiện thực hiện mặc dù?
nghiền nát

30

Bởi vì các tham số mặc định được giải quyết tại thời gian biên dịch, không phải thời gian chạy. Vì vậy, các giá trị mặc định không thuộc về đối tượng được gọi, mà thuộc về kiểu tham chiếu mà nó đang được gọi thông qua.


7

Các tham số tùy chọn giống như một sự thay thế vĩ mô từ những gì tôi hiểu. Chúng không thực sự tùy chọn theo quan điểm của phương pháp. Một yếu tố của điều đó là hành vi bạn thấy nơi bạn nhận được kết quả khác nhau nếu bạn sử dụng giao diện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.