2009-07-21 9 views
8

Tôi có một lớp có trường cần khởi tạo khi đối tượng được khởi tạo, chẳng hạn như danh sách cần được tạo trước khi các đối tượng có thể được thêm/xóa khỏi nó .Khởi tạo các trường lớp tại định nghĩa trường hoặc trong lớp constructor

public class MyClass1 
{ 
    private List<MyOtherClass> _otherClassList; 

    public MyClass1() 
    { 
     this._otherClasslist = new List<MyOtherClass>(); 
    } 
} 


public class MyClass2 
{ 
    private List<MyOtherClass> = new List<MyOtherClass>(); 

    public MyClass2() 
    { 
    } 
} 

Sự khác biệt giữa hai lớp này là gì và tại sao bạn chọn một phương thức so với lớp kia?

Tôi thường đặt trường trong hàm tạo, như trong MyClass1, vì tôi thấy dễ dàng hơn để có thể nhìn vào một nơi để xem mọi thứ xảy ra khi đối tượng được khởi tạo, nhưng có trường hợp nào nó là tốt hơn để khởi tạo một lĩnh vực trực tiếp như trong MyClass2?

+0

Họ có giá trị đã được mặc định ... không cần phải init chúng LẠI. –

Trả lời

5

Các IL được phát ra bởi trình biên dịch C# (VS2008 sp1) sẽ gần như tương đương cho cả hai trường hợp (ngay cả trong bản dựng Gỡ lỗi và Bản phát hành).

Tuy nhiên, nếu bạn cần thêm nhà xây dựng tham số mà phải mấtList<MyOtherClass>như một cuộc tranh cãi, nó sẽ khác nhau (đặc biệt là khi bạn sẽ tạo ra một số lượng lớn đáng kể của các đối tượng với nhà thầu như vậy).

Xem các ví dụ sau để thấy sự khác biệt (bạn có thể sao chép & qua để VS và xây dựng nó để xem ILS với Reflector hoặc ILDASM).

using System; 
using System.Collections.Generic; 

namespace Ctors 
{ 
    //Tested with VS2008 SP1 
    class A 
    { 
     //This will be executed before entering any constructor bodies... 
     private List<string> myList = new List<string>(); 

     public A() { } 

     //This will create an unused temp List<string> object 
     //in both Debug and Release build 
     public A(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class B 
    { 
     private List<string> myList; 

     //ILs emitted by C# compiler are identicial to 
     //those of public A() in both Debug and Release build 
     public B() 
     { 
      myList = new List<string>(); 
     } 

     //No garbage here 
     public B(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class C 
    { 

     private List<string> myList = null; 
     //In Release build, this is identical to B(), 
     //In Debug build, ILs to initialize myList to null is inserted. 
     //So more ILs than B() in Debug build. 
     public C() 
     { 
      myList = new List<string>(); 
     } 

     //This is identical to B(List<string> list) 
     //in both Debug and Release build. 
     public C(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class D 
    { 
     //This will be executed before entering a try/catch block 
     //in the default constructor 
     private E myE = new E(); 
     public D() 
     { 
      try 
      { } 
      catch (NotImplementedException e) 
      { 
       //Cannot catch NotImplementedException thrown by E(). 
       Console.WriteLine("Can I catch here???"); 
      } 
     } 
    } 

    public class E 
    { 
     public E() 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      //This will result in an unhandled exception. 
      //You may want to use try/catch block when constructing D objects. 
      D myD = new D(); 
     } 
    } 
} 

Lưu ý: tôi không thay đổi bất kỳ cờ tối ưu hóa khi chuyển sang Thả xây dựng.

0

Các mã tương đương vì trình biên dịch khởi tạo trong mọi hàm tạo trong trường hợp thứ hai, lợi ích của trường hợp thứ hai là lập trình viên sẽ không nghĩ khởi tạo trường khi thêm hàm tạo mới sau năm viết lớp đó :)

+0

Nhà phát triển ban đầu tạo lớp nên sử dụng chuỗi tạo dựng thích hợp để nó không phải là vấn đề. Tuy nhiên, các lập trình viên sửa đổi lớp một năm sau đó hiểu rõ hơn về lớp trước khi anh ta chạm vào nó. Anh ta nên đảm bảo tất cả các trường đang được khởi tạo đúng cách. –

0

Trong C# thực tế không có sự khác biệt giữa hai ví dụ của bạn. Tuy nhiên, nhà phát triển có xu hướng khởi tạo các trường của họ trong hàm tạo vì nó ít bị lỗi hơn.

2

Khi khởi tạo trong một hàm tạo, tôi cho rằng sẽ dễ nắm bắt và xử lý các ngoại lệ nếu cần thiết.

1

Trong MyClass1 ai đó có thể ghi đè lên hàm tạo và gây ra sự cố.

3

Có một sự khác biệt:

Fields được khởi động ngay lập tức trước khi các nhà xây dựng cho các trường hợp đối tượng được gọi, vì vậy nếu các nhà xây dựng gán giá trị của một trường , nó sẽ ghi đè lên bất kỳ giá trị được đưa ra trong quá trình khai báo trường. From MSDN:

3

Cả hai hành vi phải giống hệt nhau.
Tuy nhiên, điều bạn có thể muốn xem xét là một trường hợp cạnh IL - Bloat. IL cho bộ khởi tạo trường được chèn vào đầu mỗi ctor.Và từ đó, nếu bạn có nhiều trình khởi tạo trườngnhiều số điện thoại bị quá tải, cùng một phần của IL là tiền tố cho quá tải ctor của IL. Kết quả là tổng kích thước của assembly của bạn có thể tăng lên so với trường hợp bạn sử dụng constructor chain hoặc delegate với một hàm Initialize() phổ biến (trong đó IL lặp lại sẽ là một cuộc gọi phương thức). do đó cho kịch bản cụ thể này, các trình khởi tạo trường sẽ là một sự lựa chọn tương đối yếu hơn.

Bạn có thể xác minh điều này với phản xạ trên nhị phân cho mã này đoạn mã

public class MyClass2 
    { 
    private List<int> whack = new List<int>(); 
    // lots of other field initializers 

    public MyClass2() 
    { 
     Console.WriteLine("Default ctor"); 
    } 
    public MyClass2(string s) 
    { 
     Console.WriteLine("String overload"); 
    } 
     // lots of other overload ctors 
    }