từ khóa mới trong chữ ký phương pháp


113

Trong khi thực hiện tái cấu trúc, tôi đã tạo ra một phương thức như ví dụ bên dưới. Kiểu dữ liệu đã được thay đổi vì mục đích đơn giản.

Trước đây tôi đã có một câu lệnh gán như thế này:

MyObject myVar = new MyObject();

Nó đã được cấu trúc lại một cách tình cờ:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

Đây là kết quả của lỗi cắt / dán từ phía tôi, nhưng newtừ khóa trong private static newlà hợp lệ và được biên dịch.

Câu hỏi : newTừ khóa biểu thị điều gì trong một chữ ký phương thức? Tôi cho rằng đó là thứ được giới thiệu trong C # 3.0?

Làm thế nào để điều này khác với override?


5
Một số lưu ý về khả năng mong muốn của phương pháp ẩn: blog.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert

2
@Eric .. Bài hay quá. Tôi chưa bao giờ nghĩ đến phương thức ẩn theo cách đó, như vâng, đối tượng LÀ một thứ, nhưng chúng ta hiện đang trình bày nó như một thứ khác, vì vậy chúng ta muốn hành vi của thứ mà chúng ta đang trình bày nó là AS. Thông minh ...
BFree

1
Một câu hỏi trùng lặp trong tương lai và tôi đã cố gắng trả lời một cách chi tiết: Sử dụng từ khóa mới trong c #
Ken Kin

1
Việc sử dụng newas aa modifier trên một phương thức (hoặc thành viên kiểu khác) không phải là "thứ được giới thiệu trong C # 3.0". Nó đã có từ phiên bản đầu tiên của C #.
Jeppe Stig Nielsen,

1
Có 4 bản sao của câu hỏi này trong SO. Theo tôi, cái này sẽ cho bạn hiểu rõ nhất: stackoverflow.com/questions/3117838/… . Nó được trả lời bởi @EricLippert.
Raikol Amaro

Câu trả lời:


101

Tham khảo từ khóa mới từ MSDN:

Tham chiếu MSDN

Đây là một ví dụ tôi tìm thấy trên mạng từ một MVP của Microsoft có ý nghĩa tốt: Liên kết đến Bản gốc

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

B b = new B();
A a = b as A;

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

Ghi đè chỉ có thể được sử dụng trong các trường hợp rất cụ thể. Từ MSDN:

Bạn không thể ghi đè một phương thức không phải ảo hoặc tĩnh. Phương thức cơ sở được ghi đè phải là phương thức ảo, trừu tượng hoặc ghi đè.

Vì vậy, từ khóa 'mới' là cần thiết để cho phép bạn 'ghi đè' các phương thức không ảo và tĩnh.


4
tôi chỉ chạy mẫu của bạn .. nó sẽ ghi đè ngay cả khi bạn không chỉ định "mới", phải không?
Michael Sync

2
Chính xác là @MichaelSync, vậy tại sao chúng ta cần đề cập đến từ khóa mới?
ZoomIn

2
"Mặc dù bạn có thể ẩn các thành viên mà không cần sử dụng công cụ sửa đổi mới, nhưng bạn sẽ nhận được cảnh báo về trình biên dịch." mỗi tài liệu được liên kết. Vì vậy, từ khóa cho thấy rõ bạn có ý định ẩn và cảnh báo không được đưa ra.
Jim Counts

1
-1 -> nó không bắt buộc - hành vi sẽ giống nhau. Rất khó hiểu đối với một người mới biết newvề vấn đề này, nhưng tôi nghĩ rằng nó thực sự chỉ ở đó để dễ đọc - trình biên dịch chỉ đơn giản là cảnh báo bạn rằng khi bạn gọi phương thức lớp dẫn xuất cùng tên với cơ sở, bạn sẽ không nhận được phương thức của lớp cơ sở mà bạn có thể nghĩ rằng .... nó kỳ lạ ... hoàn toàn là để dễ đọc?
Don Cheadle

1
Vì lợi ích của sự hoàn chỉnh: Nếu cả lớp A và B đều triển khai một giao diện IMyInterfacethì việc triển khai trên lớp Derived sẽ được gọi. Do đó IMyInterface c = new B()sẽ gọi việc thực hiện của lớp B. Nếu chỉ có một lớp triển khai giao diện, phương thức từ lớp thực hiện nó, sẽ được gọi.
Nullius

61

Không, thực ra nó không phải là "mới" (pardon the pun). Về cơ bản, nó được sử dụng để "ẩn" một phương thức. I E:

public class Base
{
   public virtual void Method(){}
}

public class Derived : Base
{
   public new void Method(){}
}

Nếu sau đó bạn làm điều này:

Base b = new Derived();
b.Method();

Phương thức trong Cơ sở là phương thức sẽ được gọi, KHÔNG phải phương thức trong phương thức dẫn xuất.

Một số thông tin khác: http://www.akadia.com/services/dotnet_polymorphism.html

Chỉnh sửa lại của bạn: Trong ví dụ mà tôi đã đưa ra, nếu bạn muốn "ghi đè" thay vì sử dụng "mới" thì khi bạn gọi b.Method (); Phương thức của lớp Derived sẽ được gọi vì Đa hình.


public class Base {public virtual void Method () {Console.WriteLine ("Cơ sở"); }} public class Derived: Base {public void Method () {Console.WriteLine ("Derived"); }} Cơ sở b = new Derived (); b.Method (); Tôi đã nhận được "Cơ sở" .. Nếu tôi thêm "mới" trong lớp Có nguồn gốc, tôi vẫn nhận được "Cơ sở" ..
Michael Sync

@michael phương pháp vẫn là ảo
Rune FS

@MichaelSync: Bạn sẽ thấy một cảnh báo với mã đó; Cảnh báo Derived.Method () 'ẩn thành viên kế thừa' Base.Method () '. Để làm cho thành viên hiện tại ghi đè triển khai đó, hãy thêm từ khóa ghi đè. Nếu không, hãy thêm từ khóa mới.
Christopher McAtackney

2
@MichaelSync Nếu từ "ghi đè" bị bỏ qua, thì hành vi mặc định là "mới", ví dụ như ẩn phương thức. Vì vậy, việc bạn bỏ đi từ mới không có gì khác biệt.
Miễn phí 14/12/11

1
Đúng. Đó cũng là những gì tôi nghĩ. Không chắc tại sao C # lại thêm từ khóa "mới" .. nó chỉ để làm cho cảnh báo biến mất .. stackoverflow.com/questions/8502661/…
Michael Sync

22

Như những người khác đã giải thích, nó được sử dụng để ẩn một phương thức hiện có. Nó hữu ích để ghi đè một phương thức không phải là ảo trong lớp cha.

Hãy nhớ rằng việc tạo thành viên "mới" không phải là đa hình. Nếu bạn ép kiểu đối tượng đến kiểu cơ sở, nó sẽ không sử dụng thành viên của kiểu dẫn xuất.

Nếu bạn có một lớp cơ sở:

public class BaseClass
{
    public void DoSomething() { }
}

Và sau đó là lớp dẫn xuất:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

Nếu bạn khai báo một kiểu DerivedTypevà sau đó ép kiểu, phương thức DoSomething()không phải là đa hình, nó sẽ gọi phương thức của lớp cơ sở, không phải phương thức dẫn xuất.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.

1
Nó sẽ gọi phương thức từ lớp cơ sở ngay cả khi bạn xóa "mới" khỏi DerievedType ..
Michael Sync

2
Tôi nghĩ rằng đoạn thứ hai của bạn đóng đinh toàn bộ chủ đề này. Khi xử lý các cuộc gọi từ một tham chiếu lớp cơ sở , "mới" không phải là đa hình ... tức là. Bạn nhận được chính xác những gì bạn chỉ định khi thực hiện cuộc gọi. "override" là từ đa nghĩa ... tức là. Bạn nhận được những gì mà hệ thống phân cấp lớp chỉ định.
Jonathon Reinhart

Làm thế nào đây là một cái gì đó rất mơ hồ được xác định? Rất khó chịu cho một người mới. Và không có, nó không có gì để làm với đa hình - nó có hành vi tương tự chính xác như đơn giản là không có overridetrong chữ ký của phương pháp
Don Cheadle

Điều gì được che giấu từ những gì? Chắc chắn là không thể newẩn kiểu cơ sở khỏi kiểu dẫn xuất, bởi vì bạn không thể luôn luôn truy cập kiểu cơ sở bằng cách sử dụng base.<type>ký hiệu?
thatWiseGuy

@thatWiseGuy Nó "ẩn" theo nghĩa là phương thức cơ sở sẽ không được gọi khi sử dụng lớp dẫn xuất trong mã của bạn. Nó vẫn có thể được gọi trong nội bộ bằng cách sử dụng base.<method>và cũng có thể được gọi bên ngoài nếu bạn truyền đến loại cơ sở trong mã của mình. Về mặt kỹ thuật, sẽ chính xác hơn khi coi nó là "ghi đè" phương thức cơ sở hơn là "ẩn" nó.
Dan Herbert,

7

Từ các tài liệu:

Nếu phương thức trong lớp dẫn xuất được đặt trước từ khóa new, phương thức được định nghĩa là độc lập với phương thức trong lớp cơ sở.

Điều này có nghĩa là gì trong thực tế:

Nếu bạn kế thừa từ một lớp khác và bạn có một phương thức chia sẻ cùng một chữ ký, bạn có thể định nghĩa nó là 'mới' để nó độc lập với lớp cha. Điều này có nghĩa là nếu bạn có tham chiếu đến lớp 'cha' thì việc triển khai đó sẽ được thực thi, nếu bạn có tham chiếu đến lớp con thì việc triển khai đó sẽ được thực thi.

Cá nhân tôi cố gắng tránh từ khóa 'mới' vì nó thường có nghĩa là tôi đã đánh sai thứ bậc lớp của mình, nhưng đôi khi nó có thể hữu ích. Một nơi dành cho lập phiên bản và khả năng tương thích ngược.

Có rất nhiều thông tin trong MSDN cho việc này .


Thực tế là hành vi này xảy ra không liên quan gì đến việc newở đó. Đó là cú pháp / khả năng đọc.
Don Cheadle

3

Nó có nghĩa là phương thức thay thế một phương thức có cùng tên được kế thừa bởi lớp cơ sở. Trong trường hợp của bạn, có thể bạn không có phương thức có tên đó trong lớp cơ sở, có nghĩa là từ khóa mới hoàn toàn không cần thiết.


3

Truyện dài ngắn - nó KHÔNG bắt buộc, nó KHÔNG thay đổi hành vi, và nó HOÀN TOÀN ở đó để dễ đọc.

Đó là lý do tại sao trong VS, bạn sẽ thấy một chút nguệch ngoạc, nhưng mã của bạn sẽ biên dịch và chạy hoàn toàn tốt và như mong đợi.

Người ta phải tự hỏi liệu nó có thực sự đáng để tạo newtừ khóa không khi tất cả những gì nó có nghĩa là sự thừa nhận của nhà phát triển "Vâng, tôi biết tôi đang ẩn một phương thức cơ sở, vâng, tôi biết tôi không làm bất cứ điều gì liên quan đến virtualhoặcoverriden (đa hình) - Tôi thực sự muốn chỉ tạo ra phương pháp riêng của nó ".

Nó hơi kỳ lạ với tôi, nhưng có lẽ chỉ vì tôi xuất thân từ một Javanền tảng và có sự khác biệt cơ bản này giữa C#kế thừa và Java: Trong Java, các phương thức là ảo theo mặc định trừ khi được chỉ định bởi final. Trong C#, các phương thức là cuối cùng / cụ thể theo mặc định trừ khi được chỉ định bởi virtual.


1

Từ MSDN :

Sử dụng công cụ sửa đổi mới để ẩn rõ ràng một thành viên được kế thừa từ một lớp cơ sở. Để ẩn một thành viên được kế thừa, hãy khai báo nó trong lớp dẫn xuất bằng cách sử dụng cùng một tên và sửa đổi nó bằng công cụ sửa đổi mới.


0

Hãy cẩn thận với gotcha này.
Bạn có một phương thức được định nghĩa trong một giao diện được triển khai trong một lớp cơ sở. Sau đó, bạn tạo một lớp dẫn xuất ẩn phương thức của giao diện, nhưng không khai báo cụ thể lớp dẫn xuất như việc triển khai giao diện. Nếu sau đó bạn gọi phương thức thông qua một tham chiếu đến giao diện, phương thức của lớp cơ sở sẽ được gọi. Tuy nhiên, nếu lớp dẫn xuất của bạn thực hiện cụ thể giao diện, thì phương thức của nó sẽ được gọi là bất kỳ loại tham chiếu nào được sử dụng.

interface IMethodToHide
{
    string MethodToHide();
}

class BaseWithMethodToHide : IMethodToHide
{
    public string MethodToHide()
    {
        return "BaseWithMethodToHide";
    }
}

class DerivedNotImplementingInterface   : BaseWithMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedNotImplementingInterface";
    }
}

class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedImplementingInterface";
    }
}

class Program
{
    static void Main()
    {
        var oNoI = new DerivedNotImplementingInterface();
        IMethodToHide ioNoI = new DerivedNotImplementingInterface();

        Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
            + oNoI.MethodToHide());
        // calls DerivedNotImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioNoI.MethodToHide());
        // calls BaseWithMethodToHide.MethodToHide()
        Console.ReadLine();

        var oI = new DerivedImplementingInterface();
        IMethodToHide ioI = new DerivedImplementingInterface();

        Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
            + oI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.ReadLine();

    }
}
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.