2011-12-19 5 views
27
object a = new Dog(); 

vsSự khác nhau giữa đối tượng a = new Dog() vs Dog a = new Dog()

Dog a = new Dog(); 

Trong cả hai trường hợp a.GetType() cho Dog. Cả hai đều gọi cùng một hàm tạo (với cùng một cấu trúc phân cấp).

Sau đó, bạn có thể cho tôi biết sự khác biệt giữa hai câu lệnh này không?

+0

Vẻ đẹp của điều này tôi thấy là đôi khi, một phương pháp có thể cần phải làm việc với nhiều hơn một 'Dog'. Với câu lệnh 'Object a = new Dog()', bạn có thể sử dụng nó trong một phương thức. Tương tự như vậy, bạn có thể làm 'Object b = new Cat()', và bạn có thể sử dụng nó theo cùng một cách. –

+1

@Ken Tại sao không sử dụng giao diện? Giống như HousePet a = new Dog() ;. Đặt các phương thức chia sẻ trong giao diện HousePet hoặc giao diện Động vật. – Andy

+9

Đây là một bản sao ... – slugster

Trả lời

36

Cả hai đều tạo đối tượng Dog. Chỉ thứ hai cho phép bạn trực tiếp gọi các phương thức Dog hoặc để xử lý nó như một con chó, chẳng hạn như nếu bạn cần truyền đối tượng đến một phương thức làm tham số kiểu Dog (hoặc thứ gì đó trong hệ thống phân cấp Dog cụ thể hơn là chỉ đơn giản là object).

object obj = new Dog(); 
// can only see members declared on object 
var type = obj.GetType(); // can do this 
Console.WriteLine(obj.ToString()); // also this 
obj.Bark(); // Error! Bark is not a member of System.Object 

Dog dog = new Dog(); 
// can do all of the methods declared for Object 
dog.Bark(); // can finally use the method defined for Dog 
+0

Thanks.I đồng ý với bạn. Nhưng khi tôi viết mã sau đối tượng a = new Dog(); Console.WriteLine (a.GetType()); Tôi nhận ra một con chó và không phải là Object. Tại sao lại thế? – om471987

+3

@Omkarpanhalkar, nó nhận được loại cá thể, không phải của biến. Đây là một cơ chế thời gian chạy nhận siêu dữ liệu được liên kết với cá thể và thời gian chạy biết rằng cá thể là một Dog. Những gì bạn đã mất là những lợi ích biên dịch. –

+0

Lần sau khi tôi được hỏi một cái gì đó tương tự, tôi sẽ sử dụng "đối xử với nó như một xxx", đó là từ thích hợp nhất, đối với tôi như là một người bản ngữ không phải tiếng Anh. –

5

dòng đầu tiên của bạn tạo ra một biến loại object.

Trình biên dịch sẽ không cho phép bạn coi đó là Dog.

+0

trừ khi bạn truyền, ví dụ: '(Chó) a' –

+1

@PeterOlson Trong trường hợp đó nó không còn là một đối tượng' đồng bằng'… – poke

6

new Dog() là một biểu thức tạo ra một phiên bản Dog mới. Nó gọi hàm tạo không tham số của lớp Dog.

a là một biến: một ô lưu trữ trong bộ nhớ giữ tham chiếu đến cá thể Dog sau khi gán.

Sự khác biệt là một biến Dog chỉ có thể giữ một tham chiếu đến một trường hợp Dog (hoặc một thể hiện của bất kỳ lớp dẫn xuất từ ​​Dog), trong khi một biến object có thể giữ một tham chiếu đến một thể hiện object (hoặc một thể hiện của bất kỳ lớp nào có nguồn gốc từ object - mà lớp học Dog đều có).

Khi bạn có biến số Dog, bạn có thể gọi bất kỳ phương thức nào được xác định bởi lớp Dog (và các lớp cơ sở của nó) trên cá thể được tham chiếu. Khi bạn có biến số object, bạn chỉ có thể gọi các phương thức của lớp object trên cá thể.

+0

Thanks.I đồng ý với bạn. Nhưng khi tôi viết mã sau đây đối tượng a = new Dog(); Console.WriteLine (a.GetType()); Tôi nhận được một ra như Dog và không Object. Tại sao lại thế? – om471987

+1

Vì bạn gọi phương thức GetType trên cá thể 'Dog' được tham chiếu bởi' a'. GetType trả về loại cá thể, không phải của biến. – dtb

5

Cả hai câu lệnh đều chứa khai báo và lời gọi hàm tạo. Các invocations của constructor là giống hệt nhau, do đó bạn nhận được một Dog trong cả hai trường hợp. Các khai báo khác nhau: trong trường hợp đầu tiên, bạn khai báo một biến kiểu object, một siêu lớp của Dog; trong trường hợp thứ hai, bạn khai báo một biến loại Dog. Sự khác biệt là trong mã tiếp theo, bạn có thể gọi các phương thức Dog mà không có phép đúc chỉ khi bạn khai báo biến là Dog; nếu bạn tuyên bố nó là object, bạn sẽ cần một dàn diễn viên.

4

Cả hai câu lệnh liên quan đến việc gọi hàm tạo mặc định là Dog khi bạn tự đề cập đến; do đó, rõ ràng là trong cả hai trường hợp, cá thể Dog được tạo. Điều này có nghĩa là cả hai câu lệnh đều kết thúc khởi tạo biến số với một ví dụ giống hệt nhau (đây là một phần của câu lệnh sau dấu bằng).

Tuy nhiên, các câu lệnh cũng có một phần khác: khai báo của biến số (đây là một phần của câu lệnh trước dấu bằng). Trong các ngôn ngữ kiểu tĩnh như C#, mỗi biến - tổng quát hơn, bất kỳ biểu hiện - có một loại tĩnh:

object a = new Dog(); // static type: object/runtime type: Dog 
Dog b = new Dog(); // static type: Dog/runtime type: Dog 

Trình biên dịch sẽ không cho phép bạn gán giá trị cho một biến mà nó không thể chứng minh là loại tĩnh của biến, ví dụ nó sẽ không cho phép

Cat c = new Dog(); // unless Dog derives from Cat, which we know isn't true 

Vì tất cả reference types ngầm xuất phát từ System.Object, gán một Dog đến một biến kiểu tĩnh object là OK. Bạn có thể nghĩ về "loại tĩnh" như đối tượng được "khai báo là". Bạn luôn có thể xác định loại tĩnh của một cái gì đó chỉ bằng cách đọc mã nguồn; đây là cách trình biên dịch thực hiện nó.

Sau đó, cũng có loại thời gian chạy của mỗi biến (biểu thức), mà tôi đã đề cập ở trên. Điều này là như nhau trong cả hai trường hợp, bởi vì sau tất cả trong cả hai trường hợp, chúng tôi đã tạo ra một Dog. Bạn có thể nghĩ về "loại thời gian chạy" như đối tượng thực sự . Loại thời gian chạy của một cái gì đó không thể được xác định chỉ bằng cách đọc nguồn; bạn chỉ xác định nó trong khi chương trình đang chạy, do đó tên. Trong C#, điều này được thực hiện bằng cách gọi GetType.

Rõ ràng là loại thời gian chạy là thứ mà bạn không thể làm mà không có; mọi thứ đều phải "trở thành" gì đó. Nhưng tại sao lại bận tâm với việc phát minh ra khái niệm về kiểu tĩnh?

Bạn có thể nghĩ về static types làm hợp đồng giữa bạn (người lập trình) và trình biên dịch. Bằng cách khai báo kiểu tĩnh là bDog, bạn nói với trình biên dịch rằng bạn không có ý định sử dụng biến đó để lưu trữ bất kỳ thứ gì khác ngoài một số Dog. Trình biên dịch, ngược lại, hứa hẹn sẽ không cho phép bạn vi phạm mục đích đã nêu của bạn và tạo ra một lỗi nếu bạn cố gắng làm điều đó. Việc này cũng ngăn bạn sử dụng d theo bất kỳ cách nào không phải mỗi loại Dog sẽ hỗ trợ.

xem xét:

class Dog { 
    public void Woof(); 
} 

Dog d = new Dog(); 
d.Woof(); // OK 

object o = new Dog(); 
o.Woof(); // COMPILER ERROR 

Dòng cuối cùng gây ra một lỗi biên dịch vì vi phạm hợp đồng gõ tĩnh: bạn nói với trình biên dịch rằng o có thể bất cứ điều gì phát sinh từ System.Object, nhưng không phải tất cả trong những điều bắt nguồn từ đó có phương thức Woof. Vì vậy, trình biên dịch đang cố gắng bảo vệ bạn bằng cách nói "Bạn đang làm gì ở đó? Tôi không thể chứng minh rằng bất cứ điều gì trong o có thể lừa đảo! Nếu đó là một Cat thì sao?".

Ghi chú:

¹ này không có nghĩa rằng tất cả các đối tượng một cách kỳ diệu biết điều đó "là" những gì trong tất cả các ngôn ngữ. Trong một số trường hợp (ví dụ:trong C++) thông tin này có thể được sử dụng khi tạo một đối tượng, nhưng sau đó "bị lãng quên" để cho phép trình biên dịch tự do hơn để tối ưu hóa mã. Nếu điều này xảy ra, đối tượng vẫn là điều gì đó, nhưng bạn không thể poke nó và hỏi nó "bạn là gì?".

² Thực ra, trong ví dụ tầm thường này, nó có thể chứng minh điều đó. Nhưng nó sẽ không chọn sử dụng kiến ​​thức này bởi vì tôn trọng hợp đồng của loại tĩnh là toàn bộ vấn đề.

+0

Câu trả lời hay! Đây là một lời giải thích tốt. Tôi chỉ có một vài câu hỏi nhỏ. Đầu tiên, bạn nói: "Rõ ràng là kiểu thời gian chạy là thứ mà bạn không thể làm mà không có" - nhưng vẫn có các ngôn ngữ được gõ tĩnh mà không có bất kỳ sự hỗ trợ nào cho việc theo dõi các kiểu thời gian chạy. (Ví dụ, thông tin kiểu thời gian chạy, RTTI, là tùy chọn trong C++.) Để làm việc này, trình biên dịch cần một cách mù quáng tin tưởng việc sử dụng các lập trình viên: nếu bạn nói đối tượng này ở đây thực sự là Chó, thì tốt hơn là Chó lúc chạy, hoặc những điều rất xấu (hành vi không xác định) sẽ xảy ra. Nhưng nó hoạt động. –

+0

Thứ hai, nó có thể hữu ích nếu khi giải thích rằng 'GetType()' trả về kiểu thời gian chạy, bạn đã đưa ra một lời giải thích ngắn gọn về cách gửi phương thức ảo. (Off đỉnh đầu của tôi, tôi không chắc chắn rằng 'GetType()' thực sự là một cuộc gọi ảo, nhưng nó là một điểm nhảy hữu ích để giải thích khái niệm.) Toàn bộ lý do tại sao đa hình subtype hoạt động là vì ảo các phương thức - có nghĩa là, bởi vì các lời gọi phương thức có thể được gửi đi dựa trên kiểu thời gian chạy, không phải kiểu tĩnh. Trong thực tế, bạn có thể có đa hình mà không có kiểu tĩnh: chỉ cần nhìn vào Python hoặc Ruby. –

+0

@DanielPryden: Về kiểu thời gian chạy, tôi lặp lại đoạn văn để rõ ràng đối tượng vẫn * là * (ví dụ: có thể có vtable quyết định chính xác), nhưng bạn không thể yêu cầu lấy lại thông tin này.Về tính đa hình, điểm của bạn là hoàn toàn chính xác; đó chỉ đơn giản là một suy nghĩ phút cuối tồi tệ về phần của tôi, mà tôi đã muốn loại bỏ trong vài giờ qua tôi đã được AFK. – Jon

2

Điều này rất hữu ích khi bạn muốn sử dụng đa hình và bạn có thể sử dụng phương pháp trừu tượng đã triển khai trong Dog. Do đó, theo cách này, đối tượng là Chó, thậm chí là Object. Vì vậy, bạn có thể sử dụng cách này khi bạn muốn sử dụng đa hình.