คำหลักใหม่ในลายเซ็นวิธี


113

ในขณะที่ทำการปรับโครงสร้างใหม่ฉันได้สร้างวิธีการดังตัวอย่างด้านล่าง ประเภทข้อมูลได้รับการเปลี่ยนแปลงเพื่อความเรียบง่าย

ก่อนหน้านี้ฉันมีคำสั่งมอบหมายเช่นนี้:

MyObject myVar = new MyObject();

มันถูกปรับโครงสร้างใหม่โดยบังเอิญ:

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

นี่เป็นผลมาจากข้อผิดพลาดในการตัด / วางในส่วนของฉัน แต่newคำหลักในprivate static newถูกต้องและคอมไพล์

คำถาม : อะไรคือnewคำสำคัญมีความหมายอย่างไรในลายเซ็นของวิธีการ? ฉันคิดว่ามันเป็นสิ่งที่แนะนำใน C # 3.0?

สิ่งนี้แตกต่างจากoverrideอย่างไร?


5
บันทึกบางส่วนเกี่ยวกับความปรารถนาของวิธีการซ่อน: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert

2
@ เอริก.. กระทู้เยี่ยม. ฉันไม่เคยคิดว่าจะซ่อนวิธีการในลักษณะนั้นเพราะใช่วัตถุเป็นสิ่งหนึ่ง แต่ตอนนี้เรากำลังนำเสนอเป็นอย่างอื่นดังนั้นเราจึงต้องการพฤติกรรมของสิ่งที่เรานำเสนอเป็น AS ฉลาด ...
BFree

1
คำถามที่ซ้ำกันในอนาคตและฉันได้ลองตอบโดยละเอียดแล้ว: ใช้คำหลักใหม่ใน c #
Ken Kin

1
การใช้newเป็นตัวปรับแต่งในเมธอด (หรือสมาชิกประเภทอื่น) ไม่ใช่ "สิ่งที่นำมาใช้ใน C # 3.0" มันมีมาตั้งแต่รุ่นแรกของ C #
Jeppe Stig Nielsen

1
คำถามนี้มี 4 รายการที่ซ้ำกันใน SO ในความคิดของฉันสิ่งนี้จะทำให้คุณเข้าใจได้ดีที่สุด: stackoverflow.com/questions/3117838/… . ได้รับคำตอบโดย @EricLippert
Raikol Amaro

คำตอบ:


101

การอ้างอิงคำหลักใหม่จาก MSDN:

MSDN อ้างอิง

นี่คือตัวอย่างที่ฉันพบในเน็ตจาก Microsoft MVP ที่สมเหตุสมผล: ลิงก์ไปยังต้นฉบับ

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

การลบล้างสามารถใช้ได้ในกรณีที่เฉพาะเจาะจงมากเท่านั้น จาก MSDN:

คุณไม่สามารถแทนที่วิธีการที่ไม่ใช่เสมือนหรือแบบคงที่ เมธอดฐานที่ถูกแทนที่ต้องเป็นเสมือนนามธรรมหรือลบล้าง

ดังนั้นจึงจำเป็นต้องใช้คำหลัก "ใหม่" เพื่อให้คุณสามารถ "ลบล้าง" วิธีการที่ไม่ใช่เสมือนและคงที่ได้


4
ฉันเพิ่งเรียกใช้ตัวอย่างของคุณ .. มันจะลบล้างแม้ว่าคุณจะไม่ได้ระบุ "ใหม่" ใช่ไหม
Michael Sync

2
@MichaelSync แน่นอนทำไมเราต้องพูดถึงคำหลักใหม่
ZoomIn

2
"แม้ว่าคุณจะซ่อนสมาชิกได้โดยไม่ต้องใช้ตัวปรับแต่งใหม่ แต่คุณจะได้รับคำเตือนจากคอมไพเลอร์" ตามเอกสารที่เชื่อมโยง ดังนั้นคีย์เวิร์ดทำให้ชัดเจนว่าคุณตั้งใจซ่อนและไม่มีการออกคำเตือน
Jim Counts

1
-1 -> ไม่จำเป็นเลย - พฤติกรรมจะเหมือนกัน สร้างความสับสนให้กับมือใหม่เป็นอย่างมากว่าnewมันเกี่ยวกับอะไร แต่ฉันคิดว่ามันมี แต่ความสามารถในการอ่านเท่านั้น - คอมไพเลอร์กำลังเตือนคุณว่าเมื่อคุณเรียกเมธอดคลาสที่ได้รับมาซึ่งมีชื่อเดียวกับฐานคุณจะไม่ได้รับเมธอดของคลาสฐานที่คุณอาจทำได้ คิดว่า .... มันแปลก ... สำหรับการอ่านล้วนๆ?
Don Cheadle

1
เพื่อความสมบูรณ์: หากทั้งคลาส A และ B ใช้อินเทอร์เฟซIMyInterfaceการใช้งานบนคลาส Derived จะถูกเรียก ดังนั้นIMyInterface c = new B()จะเรียกการใช้งานคลาส B หากมีเพียงคลาสเดียวที่ใช้อินเทอร์เฟซระบบจะเรียกใช้เมธอดจากคลาสที่ใช้งาน
Nullius

61

ไม่จริงแล้วมันไม่ใช่ "ใหม่" (ให้อภัยเล่นสำนวน) โดยพื้นฐานแล้วจะใช้สำหรับวิธี "ซ่อน" IE:

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

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

หากคุณทำสิ่งนี้:

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

วิธีการในฐานเป็นวิธีที่จะถูกเรียกไม่ใช่วิธีที่ได้รับ

ข้อมูลเพิ่มเติมบางส่วน: http://www.akadia.com/services/dotnet_polymorphism.html

Re ของคุณแก้ไข:ในตัวอย่างที่ผมให้ไว้ถ้าคุณจะ "override" แทนการใช้ "new" เมื่อคุณเรียก b.Method (); วิธีการของคลาสที่ได้รับจะถูกเรียกเนื่องจากความหลากหลาย


คลาสสาธารณะ Base {public virtual void Method () {Console.WriteLine ("Base"); }} คลาสสาธารณะที่ได้รับ: Base {public void Method () {Console.WriteLine ("Derived"); }} ฐาน b = ได้รับใหม่ (); b.Method (); ฉันได้ "ฐาน" .. ถ้าฉันเพิ่ม "ใหม่" ในคลาส Derived ฉันยังคงได้รับ "ฐาน" ..
Michael Sync

@michael วิธีนี้ยังคงเสมือนจริง
Rune FS

@ MichaelSync: คุณควรจะเห็นคำเตือนพร้อมรหัสนั้น คำเตือนที่ได้รับวิธีการ () 'ซ่อนสมาชิกที่สืบทอดมา' Base.Method () ' ในการทำให้สมาชิกปัจจุบันแทนที่การใช้งานนั้นให้เพิ่มคีย์เวิร์ดแทนที่ หรือเพิ่มคำหลักใหม่
Christopher McAtackney

2
@MichaelSync ถ้าคำว่า "ลบล้าง" ถูกละไว้พฤติกรรมเริ่มต้นคือ "ใหม่" เช่นวิธีการซ่อน ดังนั้นการที่คุณทิ้งคำใหม่ออกไปจึงไม่เกิดความแตกต่าง
BFree

1
ใช่. นั่นคือสิ่งที่ฉันคิดเช่นกัน ไม่แน่ใจว่าเหตุใด C # จึงเพิ่มคีย์เวิร์ด "ใหม่" .. เพียงแค่ทำให้คำเตือนหายไปเท่านั้น .. stackoverflow.com/questions/8502661/…
Michael Sync

22

ตามที่คนอื่นอธิบายมันถูกใช้เพื่อซ่อนวิธีการที่มีอยู่ มีประโยชน์สำหรับการลบล้างเมธอดที่ไม่เสมือนในคลาสพาเรนต์

โปรดทราบว่าการสร้างสมาชิก "ใหม่" ไม่ใช่ความหลากหลาย หากคุณโยนวัตถุไปยังประเภทพื้นฐานวัตถุนั้นจะไม่ใช้สมาชิกของประเภทที่ได้รับ

หากคุณมีคลาสพื้นฐาน:

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

จากนั้นคลาสที่ได้รับ:

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

}

หากคุณประกาศประเภทของDerivedTypeแล้วโยนมันวิธีการDoSomething()นั้นไม่ใช่ความหลากหลายมันจะเรียกเมธอดของคลาสพื้นฐานไม่ใช่วิธีที่ได้รับ

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

1
มันจะเรียกเมธอดจากคลาสพื้นฐานแม้ว่าคุณจะลบ "ใหม่" ออกจาก DerivedType ก็ตาม ..
Michael Sync

2
ฉันคิดว่าย่อหน้าที่สองของคุณตอกย้ำหัวข้อนี้ทั้งหมด เมื่อจัดการกับการเรียกจากการอ้างอิงคลาสพื้นฐาน "ใหม่" ไม่ใช่ความหลากหลาย ... กล่าวคือ คุณจะได้รับสิ่งที่คุณระบุเมื่อโทรออก "override" คือ polymorphic ... คือ. คุณจะได้รับสิ่งที่ลำดับชั้นของคลาสระบุ
Jonathon Reinhart

สิ่งนี้มีความหมายอย่างคลุมเครืออย่างไร? น่าผิดหวังมากสำหรับมือใหม่ และไม่มันไม่มีอะไรเกี่ยวข้องกับความหลากหลาย - มันมีพฤติกรรมเหมือนกับไม่มีoverrideลายเซ็นของวิธีการ
Don Cheadle

สิ่งที่ซ่อนอยู่จากอะไร? เป็นไปไม่ได้ที่newจะซ่อนประเภทฐานจากประเภทที่ได้รับเนื่องจากคุณไม่สามารถเข้าถึงประเภทฐานโดยใช้base.<type>สัญกรณ์ได้ตลอดเวลา?
thatWiseGuy

@thatWiseGuy มัน "ซ่อน" ในแง่ที่ว่าเมธอดพื้นฐานจะไม่ถูกเรียกเมื่อใช้คลาสที่ได้รับในโค้ดของคุณ ยังคงสามารถเรียกได้ว่าเป็นการใช้งานภายในbase.<method>และยังสามารถเรียกใช้ภายนอกได้หากคุณส่งไปยังประเภทฐานในรหัสของคุณ มีความแม่นยำทางเทคนิคมากกว่าที่จะคิดว่าวิธีนี้เป็นการ "ลบล้าง" วิธีการพื้นฐานมากกว่าการ "ซ่อน"
Dan Herbert

7

จากเอกสาร:

ถ้าเมธอดในคลาสที่ได้รับนำหน้าด้วยคีย์เวิร์ดใหม่เมธอดจะถูกกำหนดให้เป็นอิสระจากเมธอดในคลาสพื้นฐาน

ความหมายในทางปฏิบัติ:

หากคุณสืบทอดจากคลาสอื่นและคุณมีวิธีการที่แชร์ลายเซ็นเดียวกันคุณสามารถกำหนดเป็น 'ใหม่' เพื่อให้เป็นอิสระจากคลาสแม่ ซึ่งหมายความว่าหากคุณมีการอ้างอิงถึงคลาส 'พาเรนต์' การใช้งานนั้นจะถูกดำเนินการหากคุณมีการอ้างอิงถึงคลาสลูกการใช้งานนั้นจะถูกดำเนินการ

โดยส่วนตัวแล้วฉันพยายามหลีกเลี่ยงคีย์เวิร์ด 'ใหม่' ตามปกติหมายความว่าฉันมีลำดับชั้นของคลาสผิด แต่ก็มีบางครั้งที่อาจมีประโยชน์ สถานที่หนึ่งสำหรับการกำหนดเวอร์ชันและความเข้ากันได้ย้อนหลัง

มีข้อมูลจำนวนมากที่อยู่ในที่ MSDN สำหรับเรื่องนี้


ความจริงที่ว่าพฤติกรรมนี้เกิดขึ้นไม่เกี่ยวข้องกับnewการมีอยู่ เป็นไวยากรณ์ / อ่านได้
Don Cheadle

3

หมายความว่าเมธอดแทนที่เมธอดด้วยชื่อเดียวกันที่สืบทอดโดยคลาสฐาน ในกรณีของคุณคุณอาจไม่มีเมธอดตามชื่อนั้นในคลาสพื้นฐานซึ่งหมายความว่าคีย์เวิร์ดใหม่นั้นไม่จำเป็นโดยสิ้นเชิง


3

เรื่องสั้นขนาดยาว - ไม่จำเป็นมันเปลี่ยนพฤติกรรมไม่และมันมีไว้เพื่อให้อ่านง่าย

นั่นเป็นเหตุผลว่าทำไมใน VS คุณจะเห็นมันกระตุกเล็กน้อย แต่โค้ดของคุณจะคอมไพล์และทำงานได้ดีและเป็นไปตามที่คาดไว้

เราต้องสงสัยว่าการสร้างnewคีย์เวิร์ดนั้นคุ้มค่าหรือไม่เมื่อผู้พัฒนายอมรับว่า "ใช่ฉันรู้ว่าฉันกำลังซ่อนวิธีการพื้นฐานใช่ฉันรู้ว่าฉันไม่ได้ทำอะไรที่เกี่ยวข้องกับvirtualหรือoverriden(ความหลากหลาย) - ฉันต้องการเพียงแค่สร้างวิธีการของตัวเอง "

เป็นบิตที่แปลกประหลาดกับผม แต่อาจจะเพียงเพราะผมมาจากJavaพื้นหลังและมีความแตกต่างนี้พื้นฐานระหว่างC#มรดกและJava: ในวิธีการนี้เป็นเสมือนโดยเริ่มต้นนอกจากที่ระบุไว้โดยJava finalในC#วิธีการเป็นที่สิ้นสุด / virtualคอนกรีตโดยปริยายนอกจากที่ระบุไว้โดย


1

จากMSDN :

ใช้ตัวปรับแต่งใหม่เพื่อซ่อนสมาชิกที่สืบทอดมาจากคลาสพื้นฐานอย่างชัดเจน หากต้องการซ่อนสมาชิกที่สืบทอดมาให้ประกาศในคลาสที่ได้รับโดยใช้ชื่อเดียวกันและแก้ไขด้วยตัวปรับแต่งใหม่


0

ระวัง gotcha นี้
คุณมีวิธีการที่กำหนดไว้ในอินเตอร์เฟสที่ใช้งานในคลาสพื้นฐาน จากนั้นคุณสร้างคลาสที่ได้รับมาซึ่งซ่อนเมธอดของอินเทอร์เฟซ แต่ไม่ได้ประกาศเฉพาะคลาสที่ได้รับมาเป็นการใช้งานอินเทอร์เฟซ หากคุณเรียกใช้เมธอดผ่านการอ้างอิงไปยังอินเตอร์เฟสเมธอดของคลาสพื้นฐานจะถูกเรียกใช้ อย่างไรก็ตามหากคลาสที่ได้รับมาของคุณใช้อินเทอร์เฟซโดยเฉพาะเมธอดของมันจะถูกเรียกว่าใช้การอ้างอิงประเภทใด

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();

    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.