อะไรคือการใช้งานจริงของโมดิฟายเออร์“ ใหม่” ใน C # เกี่ยวกับการซ่อน?


21

เพื่อนร่วมงานและฉันกำลังดูพฤติกรรมของnewคำหลักใน C # ตามที่ใช้กับแนวคิดของการซ่อน จากเอกสาร :

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

เราได้อ่านเอกสารแล้วและเราเข้าใจว่ามันใช้ทำอะไรและทำอย่างไร สิ่งที่เราไม่สามารถจัดการได้จริงๆคือทำไมคุณต้องทำตั้งแต่แรก โมดิฟายเออร์มีมาตั้งแต่ปี 2003 และเราทั้งคู่ทำงานกับ. Net มานานกว่านั้นและไม่เคยเกิดขึ้นมาก่อน

พฤติกรรมนี้จำเป็นต้องใช้ในทางปฏิบัติเมื่อใด (เช่น: นำไปใช้กับกรณีธุรกิจ) นี่เป็นคุณสมบัติที่ใช้งานได้นานกว่าประโยชน์หรือเป็นสิ่งที่ผิดปกติมากพอในสิ่งที่เราทำ (โดยเฉพาะเราทำเว็บฟอร์มและแอปพลิเคชั่น MVC และ WinForms และ WPF) ในการลองใช้คำหลักนี้และเล่นกับมันเราพบว่ามีพฤติกรรมบางอย่างที่ทำให้มันดูอันตรายเล็กน้อยหากใช้ผิดวัตถุประสงค์

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


9
บางทีคุณอาจเคยอ่านด้วย แต่มีบทความที่น่าสนใจจาก Eric Lippert (ผู้พัฒนาคอมไพเลอร์ C #) ว่าทำไมการซ่อนเมธอดจึงถูกเพิ่มไปยัง C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/ … . คำตอบนั้นเป็นส่วนหนึ่งของคำถามของคุณ แต่ฉันไม่มีกรณีธุรกิจพร้อมให้คุณดังนั้นฉันจึงใส่ความคิดเห็นไว้
Jalayn

3
@Jalayn: กรณีธุรกิจถูกกล่าวถึงโดย Eric ในบทความนี้: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

คำตอบ:


22

คุณสามารถใช้มันเพื่อเลียนแบบความแปรปรวนร่วมชนิดกลับ คำอธิบายของเอริค Lippert Eric ให้รหัสตัวอย่างนี้:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

นี่คือการแก้ไข public override Fish Contents() { ... }ไม่ถูกกฎหมายแม้ว่าจะปลอดภัย

โดยทั่วไปแล้วคุณไม่ควรใช้วิธีการซ่อนเนื่องจากมันสร้างความสับสนให้กับผู้บริโภคในชั้นเรียนของคุณ (ตัวอย่างที่เฉพาะเจาะจงด้านบนไม่ได้ประสบปัญหานี้) เพียงตั้งชื่อวิธีการใหม่ของคุณเป็นอย่างอื่นหากคุณไม่ต้องการแทนที่วิธีที่มีอยู่

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

เพียงแค่อนุญาตให้วิธีการที่ได้รับถือว่าเป็นการแทนที่จะทำให้เกิดปัญหา การเพิกเฉยต่อข้อกังวลใด ๆ กับการใช้คอมไพเลอร์วิธีการใหม่นั้นมีความแตกต่างทางความหมายจากวิธีการพื้นฐาน

สถานการณ์นี้จะกล่าวถึงในรายละเอียดในโพสต์นี้โดย Eric Lippert


1
+1 สำหรับnewการเป็นเครื่องหมายสำหรับ "สิ่งนี้น่ากลัวและสับสน"
Avner Shahar-Kashtan

ฉันคิดว่าตัวอย่างของ Eric มีประโยชน์มากถ้าเพียงเพราะฉันอยู่ในสถานการณ์ที่คล้ายกัน ... ฉันมีคลาสฐานที่ใช้วิธีการที่ไม่น่าสนใจที่ส่งคืน TBase และคลาสที่ได้รับซึ่งควรส่งคืน TDerived ในกรณีนี้มันรู้สึกเหมือนเป็นสิ่งชั่วร้ายที่จำเป็น
Kyle Baran

4

ฉันคิดว่ามันมีในกรณีที่คุณอาจต้องทำสิ่งที่นักออกแบบภาษาอาจไม่ได้คิด C # มีหลายวิธีในการตอบสนองต่อจาวารุ่นแรก และสิ่งหนึ่งที่จาวาทำก็คือให้นักพัฒนา pigeonhole อย่างชัดเจนเพื่อกำจัดความเป็นไปได้ของนักพัฒนาที่ยิงตัวเองออกมา C # ใช้วิธีที่แตกต่างออกไปเล็กน้อยและให้พลังแก่นักพัฒนามากกว่าที่จะให้นักพัฒนามีโอกาสเพิ่มขึ้นอีกเล็กน้อยในการยิงตัวเอง ตัวอย่างหนึ่งคือunsafeคำสำคัญ newคำหลักนี้เป็นอีกคำหนึ่ง

ตอนนี้มันอาจจะไม่เป็นประโยชน์เหมือนunsafeแต่เมื่อคุณได้รับข้อมูลจำเพาะภาษามันยากที่จะออกจากข้อกำหนดภาษา


5
Java ไม่มีสิ่งใหม่เนื่องจาก Java ใช้วิธีการทั้งหมดเสมือน ตามที่ Eric Lippert แรงจูงใจในการสนับสนุนใหม่คือการแก้ปัญหาระดับพื้นฐานที่เปราะบาง การมีอยู่ของสิ่งใหม่นั้นเป็นสิ่งจำเป็นสำหรับการใช้งานทางธุรกิจในโลกแห่งความเป็นจริงไม่ใช่เพียงเพื่อการใช้งานห้องสมุดระดับต่ำ Java ที่ไม่มีวิธีใหม่ (และไม่มีวิธีที่ไม่ใช่เสมือน) หมายความว่าหากคลาสฐานแนะนำวิธีการใหม่ซึ่งใช้งานอยู่แล้วภายในคลาสที่ได้รับรหัสที่มีอยู่อาจแตกได้แม้ว่าผู้พัฒนาคลาสฐานควรได้รับอนุญาต ที่จะลืมรหัสซึ่งกินมัน
Brian

3

มันบอกผู้อ่านว่า "ฉันจงใจซ่อนการใช้งานวิธีการนี้ในคลาสฐาน" โดยไม่ได้ตั้งใจ


5
สิ่งนี้ทำให้ฉันรู้สึกว่าขอทาน OP รู้ว่ามันทำอะไร แต่อยากรู้ว่าทำไม
Brian

0

คุณอาจต้องการให้สมาชิกก่อนหน้านี้ว่างผ่านชื่ออื่น:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

ไชโย


ฉันคุ้นเคยกับวิธีการทำงาน แต่คำถามของฉันคือทำไมคุณต้องการให้สมาชิกก่อนหน้านี้ใช้ชื่ออื่นด้วย? สิ่งนี้ทำลายสัญญาทุกประเภททำให้ชิ้นส่วนทั่วไปบางอย่างไร้ประโยชน์
Joel Etherton

@ Joel Etherton: ในขณะที่คุณอาจจะรู้ว่าบางครั้งนักพัฒนาต้อง "เลือก" รหัสโปรแกรมคนอื่น ๆ และเราอาจไม่ได้รับอนุญาตให้แก้ไขอาจขยายชั้นเรียน และอาจต้องใช้ทั้งสมาชิกก่อนหน้าและสมาชิกใหม่
umlcat

0

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

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