Tại sao giao diện C # không thể chứa các trường?


223

Ví dụ: giả sử tôi muốn có một ICargiao 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?


21
Các giao diện không có triển khai, vì điều này sử dụng một lớp trừu tượng, với thuộc tính Năm
Postman

6
Để thêm vào những gì đã nói ở đây, các giao diện là hợp đồng và một trường là một chi tiết triển khai trong đó nó xác định một vị trí trong bộ nhớ của máy để đặt một giá trị (con trỏ vô hướng hoặc con trỏ địa chỉ) vào.
herzmeister

5
Nhưng nếu lĩnh vực này là công khai, đó là một phần của hợp đồng và không chỉ là một chi tiết thực hiện, phải không?
Alex

Câu trả lời:


238

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.


26
Một điều tôi đôi khi bỏ lỡ là một khả năng giống như java để xác định các hằng số cấp giao diện, có lẽ sẽ không yêu cầu "khe" để hỗ trợ trong ngôn ngữ.
LBushkin

2
Tôi thích lời giải thích bằng những từ đơn giản. Cảm ơn. "CLR qua C #" và "Essential .net tập 1" cung cấp thêm chi tiết.
Sandeep GB

6
Tại sao một lĩnh vực không có một khe? và câu hỏi tương tự với các nhà khai thác? Tôi nhớ đã nghe về việc gõ vịt bằng cách sử dụng sự phản chiếu để xem liệu một giao diện được triển khai ngay cả khi lớp không kế thừa giao diện. Tại sao không thể phản chiếu (hoặc một khe) để kéo lên một trường? Tôi vẫn đang viết mã của mình vì vậy tôi có thể không cần / muốn các trường nhưng tôi đã ngạc nhiên khi thấy tôi không thể sử dụng các toán tử. Các toán tử hoàn toàn giống như các phương thức theo cách hiểu của tôi ngoại trừ không phải tất cả đều có thể bị quá tải ( An interface cannot contain constants, fields, operators. Từ msdn.microsoft.com/en-us/l

@acidzombie: Câu hỏi tại sao một giao diện không thể xác định toán tử là khác nhau (mặc dù có thể liên quan) với lý do tại sao nó không thể chứa các trường; Tôi khuyên bạn nên đăng một câu hỏi khác nếu bạn vẫn quan tâm đến điều đó.
Adam Robinson

1
@ b1nary.atr0phy tại sao với các trường? Ví dụ: nếu tôi đã khai báo một phương thức int One(), thì việc thực hiện public int One(){return 1;}không phải là một trường.
Hi-Angel

131

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
}

5
Không phải mọi thứ công khai là một phần của hợp đồng. Nếu một lớp có Năm công khai, không phải nó nói rằng hợp đồng lớp có trường loại Năm để có mặt trên đó và có thể truy cập không?
Didier A.

1
Đến bữa tiệc muộn, nhưng không, trong trường hợp này, điều đó có nghĩa là hợp đồng có Năm SỞ HỮU mà bất kỳ lớp tuân thủ nào cũng phải thực hiện. Các thuộc tính thực sự là các phương thức get / set, có trường sao lưu tự động được tạo nếu không cần logic đặc biệt. Cú pháp đặc biệt chỉ dành cho một ký hiệu rõ ràng hơn.
dùng3613916

Làm cách nào tôi có thể xác định giá trị mặc định không đổi (như 123) cho việc triển khai tự động đó Year?
lama12345

1
@ lama12345 Tôi cũng đến bữa tiệc muộn, nhưng vì C # 6 (2015, .NET Framework 4.6 và .NET Core), bạn có thể sử dụng tài sản tự động cho mục đích đó. 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; }
EriF89

56

Khai báo nó như một tài sản:

interface ICar {
   int Year { get; set; }
}

47
Câu hỏi là " Tại sao giao diện C # không thể chứa các trường?". Điều này không giải quyết điều đó.
AakashM

14
Tôi đồng ý rằng câu trả lời này không trả lời câu hỏi OP, nhưng tốt, nó đã giải quyết vấn đề của tôi.
Gorgen

36

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.


Bạn đã cung cấp tên kỹ thuật / thực tế (bảng v) cho khái niệm vị trí được đề cập bởi Eric. Cảm ơn bạn đã chi tiết Hans.
RBT

Điều gì sẽ là điểm của bảng v nếu các giao diện không được triển khai mặc định? Điều này thay đổi trong C # 8.0, nhưng đó là điểm chính.
AnthonyMonterrosa

19

Tại sao không chỉ có một Yeartà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 ICartriển khai không hợp lệ để cho phép gán cho một Yearthứ 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ó Yearcá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.


18

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:

  • Bởi vì tất cả các thành viên của một giao diện - theo định nghĩa - công khai, biến hậu thuẫn của chúng tôi hiện được hiển thị cho bất kỳ ai sử dụng giao diện
  • myBackingVariablesẽ MyClasssử 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; }
    }
}

7

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
}

3

Các giao diện không chứa bất kỳ thực hiện.

  1. Xác định một giao diện với một tài sản.
  2. Hơn nữa bạn có thể thực hiện giao diện đó trong bất kỳ lớp nào và sử dụng lớp này trong tương lai.
  3. Nếu được yêu cầu, bạn có thể có thuộc tính này được định nghĩa là ảo trong lớp để bạn có thể sửa đổi hành vi của nó.

3

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.


1
Nếu bạn cần truy cập một thành viên chỉ cần làm cho nó một tài sản và bạn sẽ tốt.
Unome

0

Đố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ó.


0

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.

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.