2011-01-28 30 views
12

Câu hỏi C# cơ bản tại đây.C# - Sự khác nhau giữa hai cách để tạo nên một thuộc tính lớp là gì?

Sự khác nhau giữa việc tạo một cá thể của một thuộc tính/trường lớp là gì khi bạn khai báo nó hoặc trong hàm tạo của đối tượng được đề cập. Ví dụ:

public class MyClass 
{ 
    public MyObject = new MyObject(); 
} 

vs

public class MyClass 
{ 
    public MyObject; 

    public MyCLass() 
    { 
     MyObject = new MyObject(); 
    } 
} 

Trả lời

23

Một lĩnh vực với một initializer được khởi trước các nhà xây dựng cơ sở được gọi là, trong khi nếu initializer là trong cơ thể, mà chỉ được thực hiện sau hàm tạo cơ bản được gọi.

Điều này có thể liên quan nếu hàm tạo cơ sở gọi một phương thức ảo - nhưng cá nhân tôi muốn tránh tình huống đó.

Mẫu mã:

public class Base 
{ 
    public Base() 
    { 
     Dump(); 
    } 

    public virtual void Dump() {} 
} 

public class Child : Base 
{ 
    private string x = "Initialized at declaration"; 
    private string y; 

    public Child() 
    { 
     y = "Initialized in constructor"; 
    } 

    public override void Dump() 
    { 
     Console.WriteLine(x); // Prints "Initialized at declaration" 
     Console.WriteLine(y); // Prints "" as y is still null 
    } 
} 
+0

+1 Ngoài ra, sự hiểu biết của tôi là rằng khởi tạo một thực tế tại tờ khai cho phép trình biên dịch/JITer để làm một số tối ưu hóa mà nó sẽ không làm khác cho một khởi tạo trong constructor. Tôi * tin rằng * Tôi đã đọc điều này trong "Nguyên tắc Thiết kế Khuôn khổ .NET" như là sự biện minh của họ cho việc khởi tạo trường như là một "thực hành tốt nhất", nhưng tôi không chắc chắn nữa. –

+0

@Cody: Không chắc chắn, phải trung thực. Cá nhân tôi sẽ không tính đến điều đó khi viết mã - tôi chỉ gắn bó với cách rõ ràng nhất để thể hiện ý định. –

+0

@Jon Skeet nhầm lẫn: Lớp con không được thừa hưởng Base trong mã – Adeel

1

Điều quan trọng cần lưu ý là (trong C#) phân initializer đến lĩnh vực này sẽ xảy ra trước khi cuộc gọi đến bất kỳ constructor lớp cơ sở (được minh chứng trong this question về việc liệu VB có thể bị buộc để làm điều tương tự).

này có nghĩa là bạn không thể sử dụng cú pháp khởi tạo để tham khảo một lĩnh vực của lớp cơ sở của bạn (ví dụ: bạn có thể không trực tiếp dịch VB này vào C#):

Public Class Base 
    Protected I As Int32 = 4 
End Class 

Public Class Class2 
    Inherits Base 

    Public J As Int32 = I * 10 
End Class 
+0

Haha, tôi vừa mới đăng bài đó như một bình luận cho câu trả lời của Jon Skeet. –

+0

-1 Điều đó không đúng, xem bài đăng của tôi –

+0

@Artur: Bạn nhận được thông tin này ở đâu xuống mà không đọc nó trước? Thử nghiệm nhỏ của bạn đã biên dịch mã trong ** C# **. Câu trả lời của Damien ở đây cũng như [câu hỏi của tôi] (http://stackoverflow.com/questions/4602468/can-vb-net-be-forced-to-initialize-instance-variables-before-invoking-the-base-ty) mà ông liên kết để chỉ ra một cách rõ ràng rằng hành vi này là khác nhau trong VB.NET **. –

2

Bạn cũng có thể sử dụng một constructor tĩnh được gọi trước khi bất kỳ constructor khác, nơi bạn có thể init biến tĩnh

public class Bus 
{ 
    private static object m_object= null; 

    // Static constructor: 
    static Bus() 
    { 
     m_object = new object(); 

     System.Console.WriteLine("The static constructor invoked."); 
    } 

    public static void Drive() 
    { 
     System.Console.WriteLine("The Drive method invoked."); 
    } 
} 

class TestBus 
{ 
    static void Main() 
    { 
     Bus.Drive(); 
    } 
} 
+0

+1 Đúng và hữu ích –

3

tôi biên dịch các mã C#:

public class MyClass1 
{ 
    public MyObject MyObject = new MyObject(); 
} 
public class MyClass2 
{ 
    public MyObject MyObject; 

    public MyClass2() 
    { 
     MyObject = new MyObject(); 
    } 
} 

tôi đã IL lắp ráp:

MyClass1:

.class public auto ansi beforefieldinit test.MyClass1 
     extends [mscorlib]System.Object 
{ 
    .field public class test.MyObject MyObject 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor() cil managed 
    { 
    // code size:  19 (0x13) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: newobj  instance void test.MyObject::.ctor() 
    IL_0006: stfld  class test.MyObject test.MyClass1::MyObject 
    IL_000b: ldarg.0 
    IL_000c: call  instance void [mscorlib]System.Object::.ctor() 
    IL_0011: nop 
    IL_0012: ret 
    } // end of method MyClass1::.ctor 

} // end of class test.MyClass1 

MyClass2:

.class public auto ansi beforefieldinit test.MyClass2 
     extends [mscorlib]System.Object 
{ 
    .field public class test.MyObject MyObject 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor() cil managed 
    { 
    // code size:  21 (0x15) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: call  instance void [mscorlib]System.Object::.ctor() 
    IL_0006: nop 
    IL_0007: nop 
    IL_0008: ldarg.0 
    IL_0009: newobj  instance void test.MyObject::.ctor() 
    IL_000e: stfld  class test.MyObject test.MyClass2::MyObject 
    IL_0013: nop 
    IL_0014: ret 
    } // end of method MyClass2::.ctor 
} // end of class test.MyClass2 

Đó là prefectly rõ ràng rằng sự khác biệt là chỉ theo thứ tự của cuộc gọi đến constructor lớp cơ sở (hệ thống. Đối tượng ::. Ctor()), Trình khởi tạo MyObject (test.MyObject ::. Ctor()) và trình khởi tạo lớp (kiểm tra lớp stfld test.MyObject.MyClass2 :: MyObject)

Trong trường hợp đầu tiên, MyClass1 khởi như sau:

  • MyObject initializer
  • lớp initializer (constructor assigment)
  • lớp cơ sở khởi tạo (cơ sở lớp constructor)

Nhưng, MyClass2 khởi tạo theo thứ tự đó:

  • lớp cơ sở khởi tạo (cơ sở lớp constructor)
  • MyObject initializer
  • lớp initializer (constructor assigment)
Các vấn đề liên quan