Tạo đối tượng mới hoặc thiết lập lại mọi thuộc tính?


29
 public class MyClass
    {
        public object Prop1 { get; set; }

        public object Prop2 { get; set; }

        public object Prop3 { get; set; }
    }

Giả sử tôi có một đối tượng myObjectcủa MyClassvà tôi cần phải thiết lập lại thuộc tính của nó, nó là tốt hơn để tạo ra một đối tượng mới hoặc phân công lại mỗi tài sản? Giả sử tôi không có bất kỳ sử dụng bổ sung với ví dụ cũ.

myObject = new MyClass();

hoặc là

myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;


14
Không thể thực sự được trả lời mà không có ngữ cảnh.
CodeInChaos

2
@CodesInChaos, thực sự. Ví dụ cụ thể trong đó việc tạo các đối tượng mới có thể không được ưa thích: Nhóm đối tượng và cố gắng giảm thiểu phân bổ để xử lý với GC.
XNargaHuntress

@ XGundam05 Nhưng ngay cả khi đó, rất có thể kỹ thuật này được đề xuất trong OP là cách xử lý thích hợp
Ben Aaronson

2
Suppose I have an object myObject of MyClass and I need to reset its properties- Nếu cần đặt lại các thuộc tính của đối tượng của bạn hoặc nếu nó hữu ích để mô hình hóa miền vấn đề, tại sao không tạo reset()phương thức cho MyClass. Xem câu trả lời của HarrisonPaine bên dưới
Brandin

Câu trả lời:


49

Khởi tạo một đối tượng mới luôn tốt hơn, sau đó bạn có 1 vị trí để khởi tạo các thuộc tính (hàm tạo) và có thể dễ dàng cập nhật nó.

Hãy tưởng tượng bạn thêm một thuộc tính mới vào lớp, bạn thà cập nhật hàm tạo hơn là thêm một phương thức mới cũng khởi tạo lại tất cả các thuộc tính.

Bây giờ, có những trường hợp bạn có thể muốn sử dụng lại một đối tượng, một trường hợp một tài sản rất tốn kém để khởi tạo lại và bạn muốn giữ nó. Tuy nhiên, đây sẽ là chuyên gia hơn và bạn có các phương pháp đặc biệt để xác định lại tất cả các thuộc tính khác. Đôi khi bạn vẫn muốn tạo một đối tượng mới ngay cả trong tình huống này.


6
Tùy chọn thứ ba có thể : myObject = new MyClass(oldObject);. Mặc dù điều này sẽ ngụ ý một bản sao chính xác của đối tượng ban đầu trong một trường hợp mới.
AmazingDreams

Tôi nghĩ rằng new MyClass(oldObject)giải pháp tốt nhất cho các trường hợp khởi tạo là tốn kém.
rickcnagy

8
Sao chép constructor là phong cách C ++ hơn. .NET dường như ủng hộ phương thức Clone () trả về một thể hiện mới đó là bản sao chính xác.
Eric

2
Công cụ có thể dễ dàng không làm gì ngoài việc gọi một phương thức Khởi tạo () công khai để bạn vẫn có một điểm khởi tạo duy nhất có thể được gọi bất kỳ điểm nào để tạo ra một đối tượng mới "mới" một cách hiệu quả. Tôi đã sử dụng các thư viện có giao diện IInitialize cho các đối tượng có thể được sử dụng trong nhóm đối tượng và được sử dụng lại để thực hiện.
Chad Schouggins

16

Bạn chắc chắn nên thích việc tạo ra một đối tượng mới trong rộng lớn đa số trường hợp. Các vấn đề với việc gán lại tất cả các thuộc tính:

  • Yêu cầu setters công khai trên tất cả các thuộc tính, điều này hạn chế đáng kể mức độ đóng gói bạn có thể cung cấp
  • Biết liệu bạn có sử dụng bổ sung cho ví dụ cũ hay không có nghĩa là bạn cần biết ở mọi nơi rằng thể hiện cũ đang được sử dụng. Vì vậy, nếu cả lớp Avà lớp Bđều được thông qua các thể hiện của lớp Cthì họ phải biết liệu chúng có được thông qua cùng một thể hiện hay không, và nếu vậy thì liệu lớp kia có còn sử dụng nó không. Điều này chặt chẽ các lớp học mà không có lý do để được.
  • Dẫn đến mã lặp đi lặp lại - như gbjbaanb đã chỉ ra, nếu bạn thêm một tham số cho hàm tạo, ở mọi nơi gọi hàm tạo sẽ không biên dịch được, sẽ không có nguy cơ bỏ sót một điểm nào. Nếu bạn chỉ cần thêm một tài sản công cộng, bạn sẽ phải chắc chắn tìm và cập nhật thủ công mọi nơi mà các đối tượng được "đặt lại".
  • Tăng độ phức tạp. Hãy tưởng tượng bạn đang tạo và sử dụng một thể hiện của lớp trong một vòng lặp. Nếu bạn sử dụng phương thức của mình, thì bây giờ bạn phải thực hiện khởi tạo riêng biệt lần đầu tiên thông qua vòng lặp hoặc trước khi vòng lặp bắt đầu. Một trong hai cách là mã bổ sung bạn phải viết để hỗ trợ hai cách khởi tạo này.
  • Có nghĩa là lớp của bạn không thể tự bảo vệ mình khỏi trạng thái không hợp lệ. Hãy tưởng tượng bạn đã viết một Fractionlớp với tử số và mẫu số, và muốn thực thi rằng nó luôn luôn giảm (tức là gcd của tử số và mẫu số là 1). Điều này là không thể thực hiện tốt nếu bạn muốn cho phép mọi người đặt công thức tử số và mẫu số, vì họ có thể chuyển qua trạng thái không hợp lệ để chuyển từ trạng thái hợp lệ này sang trạng thái hợp lệ khác. Ví dụ: 1/2 (hợp lệ) -> 2/2 (không hợp lệ) -> 2/3 (hợp lệ).
  • Hoàn toàn không phải là thành ngữ cho ngôn ngữ bạn đang làm việc, làm tăng ma sát nhận thức cho bất kỳ ai duy trì mã.

Đây là tất cả các vấn đề khá quan trọng. Và những gì bạn nhận được để đáp lại công việc làm thêm mà bạn tạo ra là ... không có gì. Nói chung, việc tạo các đối tượng là cực kỳ rẻ, vì vậy lợi ích về hiệu năng sẽ hầu như không đáng kể.

Như câu trả lời khác đã đề cập, hiệu suất thời gian duy nhất có thể là mối quan tâm có liên quan là nếu lớp của bạn thực hiện một số công việc đắt đỏ đáng kể về xây dựng. Nhưng ngay cả trong trường hợp đó, để kỹ thuật này hoạt động, bạn cần có thể tách phần đắt tiền ra khỏi các thuộc tính mà bạn đặt lại, vì vậy bạn có thể sử dụng mô hình cân bằng hoặc thay thế tương tự.


Là một lưu ý phụ, một số vấn đề ở trên có thể được giảm bớt phần nào bằng cách không sử dụng setters và thay vào đó có một public Resetphương thức trên lớp của bạn có cùng tham số như hàm tạo. Nếu vì lý do nào đó bạn đã muốn đi xuống tuyến đường thiết lập lại này, đó có lẽ sẽ là một cách tốt hơn để làm điều đó.

Tuy nhiên, sự phức tạp và lặp đi lặp lại bổ sung thêm, cùng với các điểm nêu trên mà nó không giải quyết, vẫn là một lập luận rất thuyết phục chống lại việc đó, đặc biệt là khi cân nhắc với các lợi ích không tồn tại.


Tôi nghĩ rằng một trường hợp sử dụng quan trọng về mặt đạo đức để đặt lại thay vì tạo một đối tượng mới là nếu bạn thực sự đặt lại đối tượng. I E. nếu bạn muốn các lớp khác trỏ đến đối tượng để xem một đối tượng mới sau khi bạn đặt lại nó.
Taemyr

@Taemyr Có thể, nhưng OP quy định rõ ràng trong câu hỏi
Ben Aaronson

9

Lấy ví dụ rất chung chung, thật khó để nói. Nếu "đặt lại các thuộc tính" có ý nghĩa ngữ nghĩa trong trường hợp của tên miền, nó sẽ có ý nghĩa hơn đối với người tiêu dùng của lớp bạn để gọi

MyObject.Reset(); // Sets all necessary properties to null

Hơn

MyObject = new MyClass();

Tôi KHÔNG BAO GIỜ yêu cầu làm cho người tiêu dùng của cuộc gọi lớp của bạn

MyObject.Prop1 = null;
MyObject.Prop2 = null; // and so on

Nếu lớp đại diện cho một cái gì đó có thể được thiết lập lại, nó sẽ phơi bày chức năng đó thông qua một Reset()phương thức, thay vì dựa vào việc gọi hàm tạo hoặc tự thiết lập các thuộc tính của nó.


5

Như Harrison Paine và Brandin đề xuất, tôi sẽ sử dụng lại cùng một đối tượng và nhân tố khởi tạo các thuộc tính trong phương thức Reset:

public class MyClass
{
    public MyClass() { this.Reset() }

    public void Reset() {
        this.Prop1 = whatever
        this.Prop2 = you name it
        this.Prop3 = oh yeah
    }

    public object Prop1 { get; set; }

    public object Prop2 { get; set; }

    public object Prop3 { get; set; }
}

Chính xác những gì tôi muốn trả lời. Đây là điều tốt nhất của cả hai thế giới - Và tùy thuộc vào trường hợp sử dụng, sự lựa chọn khả thi duy nhất (Instance-Pooling)
Falco

2

Nếu mẫu sử dụng dự định cho một lớp là một chủ sở hữu sẽ giữ một tham chiếu cho từng phiên bản, thì không có mã nào khác sẽ giữ các bản sao của các tham chiếu và sẽ rất phổ biến đối với các chủ sở hữu có các vòng lặp cần, nhiều lần , " điền vào "một cá thể trống, sử dụng nó tạm thời và không bao giờ cần nó nữa (một cuộc họp lớp chung như một tiêu chí như vậy StringBuilder) thì có thể hữu ích từ quan điểm hiệu năng cho lớp để bao gồm một phương thức để đặt lại một thể hiện thành như- điều kiện mới. Việc tối ưu hóa như vậy dường như không có giá trị nhiều nếu phương án thay thế chỉ yêu cầu tạo ra vài trăm trường hợp, nhưng chi phí tạo ra hàng triệu hoặc hàng tỷ trường hợp đối tượng có thể cộng lại.

Trên một lưu ý liên quan, có một vài mẫu có thể được sử dụng cho các phương thức cần trả về dữ liệu trong một đối tượng:

  1. Phương thức tạo đối tượng mới; trả về tài liệu tham khảo.

  2. Phương thức chấp nhận một tham chiếu đến một đối tượng có thể thay đổi và điền vào nó.

  3. Phương thức chấp nhận biến kiểu tham chiếu làm reftham số và sử dụng đối tượng hiện có nếu phù hợp hoặc thay đổi biến để xác định đối tượng mới.

Cách tiếp cận đầu tiên thường là dễ nhất về mặt ngữ nghĩa. Thứ hai là một chút lúng túng ở phía gọi, nhưng có thể cung cấp hiệu suất tốt hơn nếu người gọi thường xuyên có thể tạo một đối tượng và sử dụng nó hàng ngàn lần. Cách tiếp cận thứ ba hơi khó xử về mặt ngữ nghĩa, nhưng có thể hữu ích nếu một phương thức sẽ cần trả về dữ liệu trong một mảng và người gọi sẽ không biết kích thước mảng cần thiết. Nếu mã gọi giữ tham chiếu duy nhất cho mảng, viết lại tham chiếu đó với tham chiếu đến mảng lớn hơn sẽ tương đương về mặt ngữ nghĩa với việc đơn giản làm cho mảng lớn hơn (đó là hành vi mong muốn về mặt ngữ nghĩa). Mặc dù sử dụng List<T>có thể đẹp hơn so với sử dụng một mảng được thay đổi kích thước thủ công trong nhiều trường hợp, các mảng cấu trúc cung cấp ngữ nghĩa tốt hơn và hiệu suất tốt hơn so với danh sách các cấu trúc.


1

Tôi nghĩ rằng hầu hết mọi người trả lời ủng hộ việc tạo ra một đối tượng mới đều thiếu một kịch bản quan trọng: bộ sưu tập rác (GC). GC có thể có một hiệu suất thực sự trong các ứng dụng tạo ra nhiều đối tượng (nghĩ về trò chơi hoặc ứng dụng khoa học).

Giả sử tôi có một cây biểu thức đại diện cho một biểu thức toán học, trong đó trong các nút bên trong là các nút chức năng (ADD, SUB, MUL, DIV) và các nút lá là các nút cuối (X, e, PI). Nếu lớp nút của tôi có phương thức Evalu (), nó có thể sẽ gọi đệ quy cho các con và thu thập dữ liệu qua nút Dữ liệu. Ít nhất, tất cả các nút lá sẽ cần tạo một đối tượng Dữ liệu và sau đó chúng có thể được sử dụng lại trên đường lên cây cho đến khi giá trị cuối cùng được ước tính.

Bây giờ hãy nói rằng tôi có hàng ngàn cây này và tôi đã đánh giá chúng trong một vòng lặp. Tất cả các đối tượng Dữ liệu đó sẽ kích hoạt một GC và gây ra hiệu năng, một lỗi lớn (mất tới 40% mức sử dụng CPU cho một số lần chạy trong ứng dụng của tôi - Tôi đã chạy một trình lược tả để lấy dữ liệu).

Một giải pháp khả thi? Sử dụng lại các đối tượng dữ liệu đó và chỉ cần gọi một số .Reset () trên chúng sau khi bạn sử dụng xong chúng. Các nút lá sẽ không còn gọi 'Dữ liệu mới ()', họ sẽ gọi một số phương thức Factory xử lý vòng đời của đối tượng.

Tôi biết điều này bởi vì tôi có một ứng dụng đã gặp phải vấn đề này và điều này đã giải quyết 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.