Sự khác biệt giữa khai báo một phương thức trong loại cơ sở " virtual
" và sau đó ghi đè nó trong loại con bằng cách sử dụng override
từ khóa "" trái ngược với chỉ sử dụng new
từ khóa "" khi khai báo phương thức khớp trong loại con?
Sự khác biệt giữa khai báo một phương thức trong loại cơ sở " virtual
" và sau đó ghi đè nó trong loại con bằng cách sử dụng override
từ khóa "" trái ngược với chỉ sử dụng new
từ khóa "" khi khai báo phương thức khớp trong loại con?
Câu trả lời:
Từ khóa "mới" không ghi đè, nó biểu thị một phương thức mới không liên quan gì đến phương thức lớp cơ sở.
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
public class Test
{
public static void Main ()
{
Foo test = new Bar ();
Console.WriteLine (test.DoSomething ());
}
}
Điều này in sai, nếu bạn sử dụng ghi đè thì nó sẽ được in đúng.
(Mã cơ sở lấy từ Joseph Daigle)
Vì vậy, nếu bạn đang thực hiện đa hình thực sự, bạn NÊN LUÔN LUÔN . Nơi duy nhất mà bạn cần sử dụng "mới" là khi phương thức không liên quan theo bất kỳ cách nào đến phiên bản lớp cơ sở.
virtual
trong cơ sở và override
có nguồn gốc? Tại sao nó tồn tại? Mã vẫn sẽ chạy ngay cả w / o new
- vì vậy nó hoàn toàn chỉ là khả năng đọc?
Tôi luôn thấy những thứ như thế này dễ hiểu hơn với hình ảnh:
Một lần nữa, lấy mã của joseph daigle,
public class Foo
{
public /*virtual*/ bool DoSomething() { return false; }
}
public class Bar : Foo
{
public /*override or new*/ bool DoSomething() { return true; }
}
Nếu sau đó bạn gọi mã như thế này:
Foo a = new Bar();
a.DoSomething();
LƯU Ý: Điều quan trọng là đối tượng của chúng tôi thực sự là một Bar
, nhưng chúng tôi đang lưu trữ nó trong một biến kiểuFoo
(điều này tương tự như việc đúc nó)
Sau đó, kết quả sẽ như sau, tùy thuộc vào việc bạn đã sử dụng virtual
/ override
hoặc new
khi khai báo các lớp của bạn.
Đây là một số mã để hiểu sự khác biệt trong hành vi của các phương thức ảo và không ảo:
class A
{
public void foo()
{
Console.WriteLine("A::foo()");
}
public virtual void bar()
{
Console.WriteLine("A::bar()");
}
}
class B : A
{
public new void foo()
{
Console.WriteLine("B::foo()");
}
public override void bar()
{
Console.WriteLine("B::bar()");
}
}
class Program
{
static int Main(string[] args)
{
B b = new B();
A a = b;
a.foo(); // Prints A::foo
b.foo(); // Prints B::foo
a.bar(); // Prints B::bar
b.bar(); // Prints B::bar
return 0;
}
}
new
để "ẩn" phương thức cơ sở, khi đơn giản là không sử dụng ghi đè dường như làm tương tự?
virtual
và trình biên dịch sẽ phàn nàn nếu nó nhìn thấy cùng tên hàm trên một "dòng máu" không có virtual
chữ ký
Các new
từ khóa thực sự tạo ra một thành viên hoàn toàn mới mà chỉ tồn tại trên loại cụ thể.
Ví dụ
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
Phương pháp tồn tại trên cả hai loại. Khi bạn sử dụng sự phản chiếu và nhận được các thành viên của loại Bar
, bạn thực sự sẽ tìm thấy 2 phương thức được gọi là DoSomething()
trông giống hệt nhau. Bằng cách sử dụng, new
bạn có hiệu quả che giấu việc thực hiện trong lớp cơ sở, để khi các lớp xuất phát từ Bar
(trong ví dụ của tôi), phương thức gọi base.DoSomething()
đến Bar
và không Foo
.
virtual / override cho trình biên dịch biết rằng hai phương thức này có liên quan với nhau và trong một số trường hợp khi bạn nghĩ rằng bạn đang gọi phương thức (ảo) đầu tiên thì thực sự đúng khi gọi phương thức thứ hai (ghi đè). Đây là nền tảng của đa hình.
(new SubClass() as BaseClass).VirtualFoo()
Sẽ gọi phương thức VirtualFoo () của SubClass.
new nói với trình biên dịch rằng bạn đang thêm một phương thức vào một lớp dẫn xuất có cùng tên với một phương thức trong lớp cơ sở, nhưng chúng không có mối quan hệ nào với nhau.
(new SubClass() as BaseClass).NewBar()
Sẽ gọi phương thức NewBar () của BaseClass, trong khi:
(new SubClass()).NewBar()
Sẽ gọi phương thức NewBar () của SubClass.
Ngoài các chi tiết kỹ thuật, tôi nghĩ rằng việc sử dụng ảo / ghi đè truyền đạt rất nhiều thông tin ngữ nghĩa trên thiết kế. Khi bạn khai báo một phương thức ảo, bạn chỉ ra rằng bạn mong đợi rằng các lớp triển khai có thể muốn cung cấp các triển khai không mặc định của riêng chúng. Bỏ qua điều này trong một lớp cơ sở, tương tự, tuyên bố kỳ vọng rằng phương thức mặc định phải đủ cho tất cả các lớp thực hiện. Tương tự, người ta có thể sử dụng các khai báo trừu tượng để buộc các lớp thực hiện cung cấp việc thực hiện riêng của chúng. Một lần nữa, tôi nghĩ rằng điều này truyền đạt rất nhiều về cách lập trình viên mong đợi mã được sử dụng. Nếu tôi đang viết cả lớp cơ sở và lớp thực hiện và thấy mình đang sử dụng mới, tôi sẽ suy nghĩ lại một cách nghiêm túc về quyết định không biến phương thức này thành cha mẹ và tuyên bố cụ thể ý định của tôi.
new
từ khóa là để ẩn. - có nghĩa là bạn đang ẩn phương thức của mình trong thời gian chạy. Đầu ra sẽ dựa trên phương thức lớp cơ sở.override
cho ghi đè. - có nghĩa là bạn đang gọi phương thức lớp dẫn xuất của bạn với tham chiếu của lớp cơ sở. Đầu ra sẽ dựa trên phương thức lớp dẫn xuất.Phiên bản giải thích của tôi đến từ việc sử dụng các thuộc tính để giúp hiểu được sự khác biệt.
override
là đủ đơn giản, phải không? Kiểu cơ bản ghi đè lên cha mẹ.
new
có lẽ là sai lệch (đối với tôi nó là). Với các thuộc tính dễ hiểu hơn:
public class Foo
{
public bool GetSomething => false;
}
public class Bar : Foo
{
public new bool GetSomething => true;
}
public static void Main(string[] args)
{
Foo foo = new Bar();
Console.WriteLine(foo.GetSomething);
Bar bar = new Bar();
Console.WriteLine(bar.GetSomething);
}
Sử dụng một trình gỡ lỗi bạn có thể nhận thấy rằng Foo foo
có 2 GetSomething
thuộc tính, vì nó thực sự có 2 phiên bản của tài sản, Foo
's và Bar
' s, và để biết cái nào để sử dụng, c # 'cuốc' tài sản cho các loại hình hiện tại.
Nếu bạn muốn sử dụng phiên bản của Bar, bạn sẽ sử dụng ghi đè hoặc sử dụng Foo foo
thay thế.
Bar bar
chỉ có 1 , vì nó muốn hành vi hoàn toàn mới cho GetSomething
.
Không đánh dấu một phương thức bằng bất cứ điều gì có nghĩa là: Ràng buộc phương thức này bằng cách sử dụng kiểu biên dịch của đối tượng, không phải kiểu thời gian chạy (liên kết tĩnh).
Đánh dấu một phương thức bằng virtual
phương tiện: Ràng buộc phương thức này bằng cách sử dụng kiểu thời gian chạy của đối tượng, không phải kiểu thời gian biên dịch (liên kết động).
Đánh dấu một virtual
phương thức lớp cơ sở với override
trong lớp dẫn xuất có nghĩa là: Đây là phương thức được ràng buộc bằng cách sử dụng kiểu thời gian chạy của đối tượng (liên kết động).
Đánh dấu một virtual
phương thức lớp cơ sở với new
trong lớp dẫn xuất có nghĩa là: Đây là một phương thức mới, không có liên quan đến một phương thức có cùng tên trong lớp cơ sở và nó phải được ràng buộc bằng cách sử dụng loại thời gian biên dịch của đối tượng (liên kết tĩnh).
Không đánh dấu một virtual
phương thức lớp cơ sở trong lớp dẫn xuất có nghĩa là: Phương thức này được đánh dấu là new
(liên kết tĩnh).
Đánh dấu một phương thức abstract
có nghĩa là: Phương thức này là ảo, nhưng tôi sẽ không khai báo một phần thân cho nó và lớp của nó cũng trừu tượng (ràng buộc động).
new
tạo một thành viên mới có cùng tên và khiến thành viên ban đầu bị ẩn, đồng thờioverride
mở rộng việc triển khai cho thành viên được kế thừa"