2010-08-23 48 views
19

Có cách nào để trả về một thể hiện chỉ đọc của một đối tượng không?C# đối tượng chỉ đọc

public class Person 
{ 
    public String FirstName { get; set; } 
    public String LastName { get; set; } 
} 

public class SomeClass 
{ 
    public SomeClass(Person manager) 
    { 
     if (manager == null) 
      throw new ArgumentNullException("manager"); 

     _manager = manager; 
    } 

    private readonly Person _manager; 
    public Person Manager 
    { 
     get { return _manager; } //How do I make it readonly period! 
    } 
} 

Cách duy nhất để thực hiện điều này bằng cách trả về bản sao() sao cho mọi thay đổi được thực hiện cho bản sao chứ không phải bản gốc? Tôi biết đối với mảng có một hàm để trả về mảng là chỉ đọc. Oh, và tôi biết đây là một kiểu tham chiếu ... Tôi còn tự hỏi liệu có một số tính năng C# ẩn để khóa phần viết không.

Tôi đã cố gắng để đưa ra một lớp trình bao bọc chung ReadOnly, nhưng không thể tìm ra cách để có được các thuộc tính như chỉ đọc mà không làm một số phản ánh tốn kém và như vậy.

Ồ, và tôi thực sự đang cố gắng tránh tạo phiên bản thứ hai của lớp đó là tất cả các thuộc tính chỉ đọc. Tại thời điểm đó, tôi cũng có thể trả lại bản sao.

+1

Căn cứ ý kiến: Có bất kỳ nổi tiếng "ReadOnly" class generic wrapper mà là nhanh chóng và hiệu quả? Người mà tôi đã cố gắng tạo ra phải sử dụng sự phản chiếu, điều đó không thực sự là điều tôi muốn làm nếu tôi có thể tránh được nó. –

Trả lời

33

Để tiết kiệm cho mình việc tạo thêm lớp, bạn có thể làm cho nó triển khai nó như một IPerson giao diện chỉ có thuộc tính chỉ đọc.

public interface IPerson 
{ 
    string FirstName { get; } 
    string LastName { get; } 
} 
public class Person:IPerson 
{ 
    public String FirstName { get; set; } 
    public String LastName { get; set; } 
} 

public class SomeClass 
{ 
public SomeClass(Person manager) 
{ 
    if (manager == null) 
     throw new ArgumentNullException("manager"); 

    _manager = manager; 
} 

private readonly Person _manager; 
public IPerson Manager 
{ 
    get { return _manager; } //How do I make it readonly period! 
} 
} 
+1

+1, Giải pháp làm sạch cho vấn đề. –

+1

Có những lý do rất tốt để sử dụng giao diện chứ không phải là loại cụ thể (và nếu 'Person' có bất kỳ hành vi nào, tôi cũng khuyên bạn nên làm như vậy) ... nhưng làm cách nào để tiết kiệm bất kỳ nỗ lực nào so với việc tạo lớp? –

+5

Tôi thích điều này, nhưng sẽ không dễ dàng cho họ để chỉ đưa nó trở lại vào một lớp người? 'Person iCanChangeProperties = (Person) SomeClass.Manager;' Và Cast không bảo vệ chống lại họ bằng cách sử dụng tham chiếu đó để thay đổi các thuộc tính ... Tôi có thể chỉ cần dính vào bản sao. Mặc dù, giao diện ReadOnly sẽ rất đẹp nên sự phản chiếu cho chúng biết rằng chúng ** KHÔNG THỂ ** làm các thuộc tính setter. –

2

Không có tính năng nào như vậy - bạn đã bao gồm các tùy chọn của mình.

Hoặc sao chép hoặc tạo loại chỉ đọc Person. Cách tiếp cận thứ hai thường được ưa thích bởi vì ngữ nghĩa rõ ràng hơn: rõ ràng với người gọi rằng họ không nên (và không thể) sửa đổi cá thể.

+0

Cách tiếp cận này khá phổ biến trong thư viện chuẩn C#. Xem những thứ như 'String' /' StringBuilder' và 'Uri' /' UriBuilder'. Nó chỉ có ý nghĩa khi việc sử dụng chỉ đọc là nhiều hơn rất nhiều, đó là trường hợp cho hai. – Ekevoo

0

Không. Bạn đang tìm kiếm thứ gì đó như C++ - kiểu const -ness và cho variousreasons C# không có.

Tuy nhiên, các loại ẩn danh thực sự không thay đổi.

1

Không có cách nào để làm cho tất cả các thuộc tính của đối tượng của bạn chỉ đọc ra bên ngoài từ lớp. Trong ví dụ trên, bạn không thể làm cho thuộc tính _manager chỉ đọc, trừ khi bạn thay đổi các thuộc tính trong lớp Person thành chỉ đọc.

Bạn có thể thiết lập các thuộc tính của lớp Person bên trong, có nghĩa là chỉ các lớp trong cùng một assembly như Person có thể thay đổi các thuộc tính.

Hoặc, nếu bạn đặt setter của thuộc tính là riêng tư, thì chỉ mã trong Person mới có thể thay đổi giá trị của thuộc tính.

4

Bạn có thể cố định đối tượng (làm cho nó không thay đổi) trong một số điều kiện với sự trợ giúp của Castle.DynamicProxy. Đọc blog này post để biết chi tiết.

5

Bạn có thể chuyển đổi các lớp Person thành một đối tượng bất biến, như sau ..

public class Person 
{ 
    public Person(string firstName, string lastName) 
    { 
     FirstName = firstName; 
     LastName = lastName; 
    } 

    public String FirstName { get; private set; } 
    public String LastName { get; private set; } 

} 
0

đối tượng Anonymous đang read-only.

1

Đây là một ví dụ khác, dựa trên cách List.AsReadOnly được triển khai trong .NET Framework 2.0.Một boolean (IsReadOnly) được sử dụng trong các phương pháp thích hợp để ngăn chặn cập nhật:

public class Person 
{ 
    private string _firstName; 
    public string FirstName 
    { 
     get { return _firstName; } 
     set 
     { 
      if (!IsReadOnly) _firstName = value; 
      else throw new AccessViolationException("Object is read-only."); 
     } 
    } 

    private string _lastName; 
    public string LastName 
    { 
     get { return _lastName; } 
     set 
     { 
      if (!IsReadOnly) _lastName = value; 
      else throw new AccessViolationException("Object is read-only."); 
     } 
    } 

    internal virtual bool IsReadOnly { get { return false; } } 

    public ReadOnlyPerson AsReadOnly() 
    { 
     return new ReadOnlyPerson(this); 
    } 

    public class ReadOnlyPerson : Person 
    { 
     private Person _person; 
     internal override bool IsReadOnly { get { return true; } } 

     internal ReadOnlyPerson(Person person) // Contructor 
     { 
      this._person = person; 
     } 
    } 
} 

Để kiểm tra nó:

static void Main(string[] args) 
{ 
    Person p1 = new Person(); 
    p1.FirstName = "Joe"; 
    p1.LastName = "Bloe"; 
    Console.WriteLine("First = {0} Last = {1}", p.FirstName, p.LastName); 

    var p2 = p1.AsReadOnly(); 
    p2.FirstName = "Josephine"; // AccessViolationException 
} 
Các vấn đề liên quan