Ví dụ: giả sử tôi muốn có một ICar
giao diện và tất cả các cài đặt sẽ chứa trường Year
. Điều này có nghĩa là mọi thực hiện phải khai báo riêng Year
? Sẽ không đẹp hơn nếu chỉ định nghĩa điều này trong giao diện?
Ví dụ: giả sử tôi muốn có một ICar
giao diện và tất cả các cài đặt sẽ chứa trường Year
. Điều này có nghĩa là mọi thực hiện phải khai báo riêng Year
? Sẽ không đẹp hơn nếu chỉ định nghĩa điều này trong giao diện?
Câu trả lời:
Mặc dù nhiều câu trả lời khác là đúng ở cấp độ ngữ nghĩa, tôi thấy thật thú vị khi tiếp cận các loại câu hỏi này từ cấp độ chi tiết thực hiện.
Một giao diện có thể được coi là một tập hợp các vị trí , chứa các phương thức . Khi một lớp thực hiện một giao diện, lớp được yêu cầu báo cho bộ thực thi cách điền vào tất cả các vị trí cần thiết. Khi bạn nói
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
lớp nói "khi bạn tạo một thể hiện của tôi, hãy đưa một tham chiếu đến Foo.M vào vị trí cho IFoo.M.
Sau đó, khi bạn thực hiện một cuộc gọi:
IFoo ifoo = new Foo();
ifoo.M();
trình biên dịch tạo mã có nội dung "hỏi đối tượng phương thức nào trong khe cho IFoo.M và gọi phương thức đó.
Nếu một giao diện là một tập hợp các vị trí có chứa các phương thức, thì một số vị trí đó cũng có thể chứa các phương thức get và set của một thuộc tính, các phương thức get và set của một bộ chỉ mục và các phương thức thêm và xóa của một sự kiện. Nhưng một lĩnh vực không phải là một phương pháp . Không có "vị trí" nào được liên kết với một trường mà sau đó bạn có thể "điền" vào một tham chiếu đến vị trí trường. Và do đó, các giao diện có thể định nghĩa các phương thức, thuộc tính, bộ chỉ mục và sự kiện, nhưng không phải là các trường.
An interface cannot contain constants, fields, operators
. Từ msdn.microsoft.com/en-us/l
int One()
, thì việc thực hiện public int One(){return 1;}
không phải là một trường.
Các giao diện trong C # được dự định để xác định hợp đồng mà một lớp sẽ tuân thủ - không phải là một triển khai cụ thể.
Theo tinh thần đó, các giao diện C # cho phép các thuộc tính được xác định - mà người gọi phải cung cấp một triển khai cho:
interface ICar
{
int Year { get; set; }
}
Các lớp triển khai có thể sử dụng các thuộc tính tự động để đơn giản hóa việc triển khai, nếu không có logic đặc biệt nào liên quan đến thuộc tính:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year
?
public int Year => 123;
. Tuy nhiên, trong trường hợp này sẽ không có ý nghĩa gì khi có setter, vì vậy giao diện sẽ phải được xác định bằngint Year { get; }
Eric Lippert đóng đinh nó, tôi sẽ sử dụng một cách khác để nói những gì anh ấy nói. Tất cả các thành viên của một giao diện là ảo và tất cả chúng cần được ghi đè bởi một lớp kế thừa giao diện. Bạn không viết rõ ràng từ khóa ảo trong khai báo giao diện, cũng không sử dụng từ khóa ghi đè trong lớp, chúng được ngụ ý.
Từ khóa ảo được triển khai trong .NET với các phương thức và cái gọi là bảng v, một mảng các con trỏ phương thức. Từ khóa ghi đè lấp đầy vị trí bảng v bằng một con trỏ phương thức khác, ghi đè lên vị trí được tạo bởi lớp cơ sở. Các thuộc tính, sự kiện và bộ chỉ mục được thực hiện như các phương thức dưới mui xe. Nhưng lĩnh vực thì không. Do đó, các giao diện không thể chứa các trường.
Tại sao không chỉ có một Year
tài sản, đó là hoàn toàn tốt?
Các giao diện không chứa các trường vì các trường biểu thị một triển khai cụ thể của biểu diễn dữ liệu và phơi bày chúng sẽ phá vỡ đóng gói. Do đó, có một giao diện với một trường sẽ thực sự được mã hóa thành một triển khai thay vì một giao diện, đó là một nghịch lý gây tò mò cho một giao diện!
Chẳng hạn, một phần trong Year
đặc tả của bạn có thể yêu cầu người ICar
triển khai không hợp lệ để cho phép gán cho một Year
thứ muộn hơn năm hiện tại + 1 hoặc trước năm 1900. Không có cách nào để nói rằng nếu bạn có Year
các trường tiếp xúc - tốt hơn nhiều để sử dụng tài sản thay vì để làm công việc ở đây.
Câu trả lời ngắn gọn là có, mỗi loại thực hiện sẽ phải tạo biến sao lưu riêng. Điều này là do một giao diện tương tự như hợp đồng. Tất cả những gì nó có thể làm là chỉ định các đoạn mã có thể truy cập công khai cụ thể mà một kiểu triển khai phải có sẵn; nó không thể chứa bất kỳ mã nào.
Xem xét kịch bản này bằng cách sử dụng những gì bạn đề xuất:
public interface InterfaceOne
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public interface InterfaceTwo
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public class MyClass : InterfaceOne, InterfaceTwo { }
Chúng tôi có một vài vấn đề ở đây:
myBackingVariable
sẽ MyClass
sử dụng?Cách tiếp cận phổ biến nhất được thực hiện là khai báo giao diện và một lớp trừu tượng barebones thực hiện nó. Điều này cho phép bạn linh hoạt kế thừa từ lớp trừu tượng và nhận triển khai miễn phí hoặc thực hiện rõ ràng giao diện và được phép kế thừa từ lớp khác. Nó hoạt động như thế này:
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
Những người khác đã đưa ra 'Tại sao', vì vậy tôi sẽ chỉ thêm rằng giao diện của bạn có thể xác định Điều khiển; nếu bạn bọc nó trong một tài sản:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}
Các giao diện không chứa bất kỳ thực hiện.
Rất nhiều điều đã được nói, nhưng để làm cho nó đơn giản, đây là của tôi. Các giao diện được dự định có các hợp đồng phương thức được thực hiện bởi người tiêu dùng hoặc các lớp và không có các trường để lưu trữ các giá trị.
Bạn có thể lập luận rằng sau đó tại sao tài sản được phép? Vì vậy, câu trả lời đơn giản là - các thuộc tính được định nghĩa bên trong chỉ là các phương thức.
Đối với điều này, bạn có thể có một lớp cơ sở Xe thực hiện trường năm và tất cả các triển khai khác có thể kế thừa từ nó.
Một giao diện xác định các thuộc tính và phương thức công khai . Các trường thường là riêng tư hoặc nội bộ được bảo vệ, nội bộ hoặc được bảo vệ nhiều nhất (thuật ngữ "trường" thường không được sử dụng cho bất kỳ điều gì công khai).
Như đã nêu trong các câu trả lời khác, bạn có thể định nghĩa một lớp cơ sở và định nghĩa một thuộc tính được bảo vệ mà tất cả những người thừa kế có thể truy cập được.
Một điều kỳ lạ là trên thực tế một giao diện có thể được định nghĩa là nội bộ nhưng nó hạn chế tính hữu dụng của giao diện và nó thường được sử dụng để xác định chức năng bên trong không được sử dụng bởi mã bên ngoài khác.