2012-11-28 37 views
9

Tôi đang cố gắng tìm ra cách tốt để tiếp cận điều này. Tôi có một lớp khách hàng thực hiện giao diện ICustomer. Giao diện này có một số thuộc tính trong đó:Người định cư bất động sản nội bộ tại C#

public interface ICustomer 
{ 

    string FirstName {get; set;} 
    string LastName {get; set;} 
} 

Tôi chỉ muốn một số lớp nhất định có khả năng đặt các thuộc tính đó; cụ thể là, các lớp đó trong dự án. Vì vậy, tôi nghĩ về việc setter internal:

public class Customer : ICustomer 
{ 

    string FirstName {get; internal set;} 
    string LastName {get; internal set;} 
} 

tôi muốn đánh dấu rằng setter như nội bộ trong giao diện tuy nhiên, vì vậy không có cơ hội thực hiện một ai đó ICustomer và ai đó bên ngoài lắp ráp sẽ thay đổi các thuộc tính. Có cách nào tốt để làm điều này?

Trả lời

16

Thuộc tính trong giao diện chỉ nên đọc. Nó có thể chấp nhận được đối với lớp bê tông thực hiện giao diện để có một setter ngay cả khi không có gì được định nghĩa trong giao diện.

public interface ICustomer 
{ 
    string FirstName { get; } 
    string LastName { get; } 
} 

public class Customer : ICustomer 
{ 
    public string FirstName { get; internal set; } 
    public string LastName { get; internal set; } 
} 

Nếu nó thực sự quan trọng là các setter được tiếp xúc thông qua một giao diện, thay vì phải giao diện được hoàn toàn read-only, bạn có thể sử dụng một cái gì đó như thế này:

public interface IReadCustomer 
{ 
    string FirstName { get; } 
    string LastName { get; } 
} 

internal interface IWriteCustomer 
{ 
    string FirstName { set; } 
    string LastName { set; } 
} 

internal interface IReadWriteCustomer : IReadCustomer, IWriteCustomer 
{ } 

public class Customer : IReadWriteCustomer 
{ 
    private string _firstName; 
    private string _lastName; 

    public string FirstName 
    { 
     get { return _firstName; } 
     internal set { _firstName = value; } 
    } 
    public string LastName 
    { 
     get { return _lastName; } 
     internal set { _lastName = value; } 
    } 

    string IReadCustomer.FirstName 
    { 
     get { return FirstName; } 
    } 

    string IReadCustomer.LastName 
    { 
     get { return LastName; } 
    } 

    string IWriteCustomer.FirstName 
    { 
     set { FirstName = value; } 
    } 

    string IWriteCustomer.LastName 
    { 
     set { LastName = value; } 
    } 
} 
+0

Cảm ơn bạn đã hỗ trợ. Nếu tôi làm điều đó tuy nhiên, và lớp tiêu thụ sử dụng ICustomer nó không thể đến được setter. – larryq

+2

@larryq Đó là toàn bộ vấn đề, bởi vì giao diện không cam kết với setter. Nếu bạn muốn thực hiện một giao diện nội bộ thứ hai cho biết thêm các setter bạn có thể. Tôi thường có một loạt các giao diện: đây là một ví dụ thực tế: 'IDimensionedMap' chỉ cam kết kích thước của một thực thể 2D; 'IReadMap' với các thuộc tính có thể đọc được, 'IReadMap: IDimensionedMap' cho các bản đồ chỉ đọc hoặc được tính toán, và sau đó là' IWriteMap: IReadMap'. Nhưng đây là một nhóm các lớp tiện ích mở rộng phức tạp. (Đây là hai không gian Hilbert 2d, không phải từ điển, nếu bạn tò mò.) – SAJ14SAJ

+1

@larryq Trong khi tôi không chắc liệu bạn có cần hay không, tôi đã thêm một thực thi thay thế trong đó giao diện định nghĩa một phương thức thiết lập bên trong. – Servy

6

Tôi muốn như đánh dấu rằng setter là nội bộ trong giao diện tuy nhiên, do đó, không có cơ hội ai đó thực hiện ICustomer và ai đó bên ngoài lắp ráp sửa đổi những thuộc tính. Có cách nào tốt để làm điều này?

Không. Thành viên bất động sản là luôn là công khai, thật không may. Ngoài ra, rối tung xung quanh với mức truy cập vào các thuộc tính mà một phần của nó được chỉ định trên giao diện bị đau đớn, IIRC. Những gì bạn có thể làm là thế này:

public interface ICustomer 
{ 
    string FirstName { get; } 
    string SecondName { get; } 
} 

internal interface ICustomerWithSetMethods : ICustomer 
{ 
    void SetFirstName(string name); 
    void SetLastName(string name); 
} 

public class Customer : ICustomerWithSetMethods 

Sau đó từ bên ngoài nó sẽ trông giống như Customer chỉ thực hiện ICustomer, nhưng từ bên mã của bạn sẽ thấy rằng nó thực hiện ICustomerWithSetMethods.

Thật không may điều đó không chơi độc đáo nếu API của bạn cần phải khai báo bất kỳ phương pháp nào nơi bạn muốn thực sự như chỉ cần khai báo một kiểu trả về của ICustomer, nhưng bạn sẽ thực sự biết rằng nó luôn luôn ICustomerWithSetMethods.

Giả sử bạn vẫn muốn cho phép triển khai nhiều, bạn có thể có khả năng đi cho một lớp trừu tượng thay vì:

public abstract class CustomerBase 
{ 
    public abstract string FirstName { get; } 
    public abstract string LastName { get; } 

    internal abstract void SetFirstName(string name); 
    internal abstract void SetLastName(string name); 
} 

Bây giờ chúng ta có kỳ quặc nhẹ mà không ai bên ngoài lắp ráp có thể mở rộng CustomerBase của bạn, bởi vì có là phương pháp trừu tượng mà họ phải ghi đè để họ thậm chí không thể xem - nhưng điều đó có nghĩa là bạn có thể sử dụng CustomerBase ở mọi nơi trong API của mình.

Đây là phương pháp chúng tôi đã thực hiện trong Noda Time cho hệ thống lịch ở cuối - I blogged about it khi tôi lần đầu tiên đưa ra kế hoạch. Tôi nói chung là thích giao diện cho các lớp trừu tượng, nhưng lợi ích ở đây là đáng kể.

+0

Bạn đã bỏ lỡ các loại và phạm vi trên FirstName và SecondName –

+0

@JonB: Vì vậy, tôi đã làm. Cố định, cảm ơn. –

+0

@JonSkeet Tôi đồng ý với cách tiếp cận của bạn, nhưng tôi thường thực hiện cả giao diện và các lớp cơ sở trừu tượng khi bảo trì theo thời gian sẽ là một vấn đề. Một số công cụ như Resharper làm cho nó dễ dàng hơn, và điều đó có nghĩa là bạn có thể có nhiều hơn một triển khai trừu tượng khi bạn có một nhóm đối tượng phức tạp, có lẽ một đối tượng sẽ được mở rộng ra ngoài thư viện mã của riêng bạn. – SAJ14SAJ