เหตุใด C # ไม่อนุญาตให้ใช้วิธีการแบบคงที่เพื่อใช้ส่วนต่อประสาน


447

เหตุใด C # จึงออกแบบวิธีนี้

ตามที่ฉันเข้าใจอินเทอร์เฟซจะอธิบายพฤติกรรมเท่านั้นและมีจุดประสงค์ในการอธิบายข้อผูกพันตามสัญญาสำหรับคลาสที่ใช้อินเทอร์เฟซที่มีการใช้งานพฤติกรรมบางอย่าง

หากคลาสต้องการใช้พฤติกรรมนั้นในวิธีการแชร์ทำไมถึงไม่ควรใช้

นี่คือตัวอย่างของสิ่งที่ฉันมีอยู่ในใจ:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }

6
Java 8 มีอยู่แล้ว ( stackoverflow.com/questions/23148471/… )
เหลียง

1
ดูว่าคุณสามารถรวมพฤติกรรมแบบสแตติกเข้ากับการสืบทอดหรือการใช้อินเตอร์เฟสได้อย่างไร: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
IListItem.ScreenName() => ScreenName()(ใช้ไวยากรณ์ C # 7) จะใช้วิธีการอินเทอร์เฟซอย่างชัดเจนโดยการเรียกวิธีการคงที่ สิ่งที่น่าเกลียดเมื่อคุณเพิ่มการสืบทอดไปที่แม้ว่า (คุณต้องปรับใช้อินเทอร์เฟซ)
Jeroen Mostert

2
เพียงแค่ให้ทุกคนรู้ว่าการรอคอยสิ้นสุดลงแล้ว! C # 8.0 มีวิธีอินเตอร์เฟซแบบคงที่: dotnetfiddle.net/Lrzy6y (แม้ว่าพวกเขาจะทำงานแตกต่างกันเล็กน้อยกับวิธีที่ OP ต้องการให้พวกเขาทำงาน - คุณไม่ต้องใช้พวกเขา)
Jamie Twells

คำตอบ:


221

สมมติว่าคุณถามว่าทำไมคุณไม่ทำเช่นนี้:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

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


ในการใช้ตัวอย่างของคุณฉันจะให้ Animal เป็นคุณสมบัติ const ซึ่งจะยังอนุญาตให้สามารถเข้าถึงได้จากบริบทแบบสแตติกและคืนค่านั้นในการนำไปใช้

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

สำหรับสถานการณ์ที่ซับซ้อนมากขึ้นคุณสามารถประกาศวิธีคงที่และมอบหมายให้กับวิธีนั้น ในการลองทำกับตัวอย่างฉันไม่สามารถนึกถึงเหตุผลใด ๆ ที่คุณจะทำสิ่งที่ไม่สำคัญทั้งในบริบทที่คงที่และอินสแตนซ์ดังนั้นฉันจะสำรอง FooBar blob ให้คุณและใช้เป็นตัวบ่งชี้ว่ามันอาจ ไม่ใช่ความคิดที่ดี


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

12
มีกรณีที่สิ่งนี้อาจเป็นประโยชน์ ตัวอย่างเช่นฉันต้องการให้ผู้ดำเนินการทั้งหมดใช้วิธีการ GetInstance ที่รับอาร์กิวเมนต์ XElement ฉันไม่สามารถกำหนดได้ว่าเป็นวิธีการคงที่ในส่วนต่อประสานและไม่ต้องการตัวสร้างลายเซ็นจากส่วนต่อประสาน
oleks

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

5
คุณสามารถนำชิ้นส่วนไปใช้เป็นวิธีการขยายไปยังส่วนต่อประสานพื้นฐานได้เช่นเดียวกับใน: public static object MethodName(this IBaseClass base)ภายในคลาสแบบคงที่ อย่างไรก็ตามข้อเสียคือไม่เหมือนกับการสืบทอดอินเตอร์เฟส - สิ่งนี้ไม่ได้บังคับ / อนุญาตให้ผู้สืบทอดแต่ละคนแทนที่วิธีการที่ดี
ทรอย Alford

8
มันจะทำให้รู้สึกมากกับยาชื่อสามัญ ตัวอย่างเช่น void Something <T> () โดยที่ T: ISomeInterface {new T () DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I

173

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

ด้วยเหตุผลทางการเมืองฉันจะพูดถึง Eric Lippert (ผู้ซึ่งเป็นผู้รวบรวมเรียบเรียงและจบปริญญาตรีคณิตศาสตร์วิทยาศาสตร์คอมพิวเตอร์และคณิตศาสตร์ประยุกต์จาก University of Waterloo (ที่มา: LinkedIn ):

... หลักการออกแบบหลักของวิธีการคงที่, หลักการที่ให้พวกเขาชื่อ ... [คือ] ... มันสามารถกำหนดได้อย่างแน่นอนในเวลารวบรวม, วิธีการใดจะเรียกว่า นั่นคือวิธีการนี้สามารถแก้ไขได้โดยการวิเคราะห์โค้ดแบบคงที่เท่านั้น

โปรดทราบว่า Lippert ออกจากห้องสำหรับวิธีการพิมพ์ที่เรียกว่า:

นั่นคือวิธีการที่เกี่ยวข้องกับประเภท (เช่นคงที่) ซึ่งไม่ได้ใช้อาร์กิวเมนต์ "นี้" ที่ไม่เป็นโมฆะ (ไม่เหมือนอินสแตนซ์หรือเสมือน) แต่วิธีการที่เรียกว่าจะขึ้นอยู่กับชนิดที่สร้างขึ้นของ T ( แตกต่างจากแบบคงที่ซึ่งจะต้องกำหนดได้ในเวลารวบรวม)

แต่ยังไม่มั่นใจในประโยชน์ของมัน


5
ยอดเยี่ยมนี่คือคำตอบที่ฉันต้องการเขียน - ฉันไม่ทราบรายละเอียดการใช้งาน
Chris Marasti-Georg

5
คำตอบที่ดี และฉันต้องการ 'วิธีพิมพ์' นี้! จะเป็นประโยชน์ในหลาย ๆ ครั้ง (คิดเกี่ยวกับข้อมูลเมตาสำหรับประเภท / คลาส)
ฟิลิป Daubmeier

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

2
+1 สำหรับการตอบคำถามจริง ๆ แล้วไม่ใช่คนอวดดีและไร้สาระ เช่นเดียวกับ Ian Boyd กล่าวว่า: "มันไม่ใช่การออกแบบที่ไม่ดี - เป็นข้อ จำกัด ทางเทคนิค"
Joshua Pech

3
เป็นไปได้ทั้งหมดในการสร้างวัตถุสำหรับคลาสแบบสแตติกที่มี vtable ที่เกี่ยวข้อง ดูว่า Scala จัดการกับobjects อย่างไรและอนุญาตให้ใช้อินเทอร์เฟซได้อย่างไร
เซบาสเตียนกราฟ

97

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

สมมติว่าเรามีพารามิเตอร์ประเภทในวิธีการทั่วไปและเราจำเป็นต้องดำเนินการบางอย่างกับมัน เราไม่ต้องการที่จะยกตัวอย่างทันทีเพราะเราไม่รู้ถึงตัวสร้าง

ตัวอย่างเช่น:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

น่าเสียดายที่ฉันสามารถหาทางเลือก "น่าเกลียด" ได้เท่านั้น:

  • ใช้การสะท้อนที่ น่าเกลียดและเอาชนะแนวคิดของอินเทอร์เฟซและความหลากหลาย

  • สร้างคลาสโรงงานแยกต่างหากอย่างสมบูรณ์

    สิ่งนี้อาจเพิ่มความซับซ้อนของรหัสอย่างมาก ตัวอย่างเช่นหากเราพยายามจำลองวัตถุโดเมนแต่ละวัตถุจะต้องมีคลาสที่เก็บข้อมูลอื่น

  • อินสแตนซ์แล้วเรียกวิธีการอินเทอร์เฟซที่ต้องการ

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

ตัวอย่าง:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

เพื่อใช้อินสแตนซ์สำหรับการแก้ปัญหาส่วนต่อประสานแบบคงที่เราจำเป็นต้องทำสิ่งต่อไปนี้:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

เห็นได้ชัดว่าน่าเกลียดและไม่จำเป็นต้องทำให้โค้ดซับซ้อนสำหรับวิธีอื่น ๆ ทั้งหมด เห็นได้ชัดว่าไม่ใช่ทางออกที่สง่างามเช่นกัน!


35
+1 สำหรับ "คำตอบส่วนใหญ่ที่นี่ดูเหมือนจะพลาดจุดทั้งหมด"; มันเหลือเชื่อว่ามันดูเหมือนว่าคำตอบทั้งหมดค่อนข้างจะหลบหลักของคำถามที่จะเจาะลึกการใช้คำฟุ่มเฟือยที่ไม่จำเป็นส่วนใหญ่ ...

5
@Chris นี่คือตัวอย่างที่เฉพาะเจาะจงที่ทำให้ฉันเพิ่งกดข้อ จำกัด นี้อีกครั้ง ฉันต้องการเพิ่มอินเทอร์เฟซ IResettable ให้กับคลาสเพื่อระบุว่าแคชข้อมูลบางอย่างในตัวแปรสแตติกที่สามารถรีเซ็ตได้โดยผู้ดูแลไซต์ (เช่นรายการหมวดหมู่คำสั่งชุดอัตราภาษีมูลค่าเพิ่มรายการหมวดหมู่ที่ดึงมาจาก API ภายนอก) เพื่อลด DB และ API ที่นิยมภายนอกสิ่งนี้จะใช้การรีเซ็ตด้วยวิธีสแตติกอย่างเห็นได้ชัด สิ่งนี้ทำให้ฉันสามารถตรวจจับคลาสที่สามารถรีเซ็ตได้โดยอัตโนมัติ ฉันยังคงสามารถทำสิ่งนี้ได้ แต่วิธีนี้ไม่ได้บังคับใช้หรือเพิ่มใน IDE โดยอัตโนมัติและต้องอาศัยความหวัง
mattmanser

6
@ Chris ฉันไม่เห็นด้วย overkill มาก มันเป็นสัญลักษณ์ของความบกพร่องด้านภาษาเสมอเมื่อสถาปัตยกรรมมากขึ้นเป็นทางออกที่ดีที่สุด จำรูปแบบทั้งหมดที่ไม่มีใครพูดถึงอีกต่อไปตั้งแต่ C # มีข้อมูลทั่วไปและวิธีการไม่ระบุชื่อ?
mattmanser

3
คุณไม่สามารถใช้ "โดยที่ T: IQueryable, T: IDoSomeStaticMath" หรือคล้ายกันได้หรือไม่?
Roger Willcocks

2
@ ChrisMarasti-Georg: ค่อนข้างสกปรก แต่ที่น่าสนใจทางรอบมันเป็นแบบนี้การก่อสร้างน้อย: public abstract class DBObject<T> where T : DBObject<T>, new()แล้วให้ทุกชั้นเรียน DB DBObject<T>ได้รับมรดกเป็น จากนั้นคุณสามารถทำให้การดึงข้อมูลโดยใช้ฟังก์ชั่นแบบคงที่ด้วย return type T ใน abstract superclass ทำให้ฟังก์ชันนั้นสร้างวัตถุใหม่ของ T แล้วเรียกการป้องกันString GetRetrieveByKeyQuery()(กำหนดเป็นนามธรรมบน superclass) บนวัตถุนั้นเพื่อรับ แบบสอบถามจริงเพื่อดำเนินการ แม้ว่าสิ่งนี้อาจจะได้รับหัวข้อตาด
Nyerguds

20

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

string DoSomething <T> () โดยที่ T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

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

  1. สร้างคลาสทั่วไปแบบสแตติกซึ่งพารามิเตอร์ประเภทจะเป็นประเภทที่คุณจะส่งไปยัง DoSomething ข้างต้น แต่ละรูปแบบของคลาสนี้จะมีสมาชิกแบบสแตติกหนึ่งรายการหรือมากกว่าที่เกี่ยวข้องกับประเภทนั้น ข้อมูลนี้สามารถระบุได้ด้วยการให้แต่ละคลาสที่สนใจเรียกรูทีน "register information" หรือโดยใช้ Reflection เพื่อรับข้อมูลเมื่อตัวสร้างสแตติกของคลาสที่เปลี่ยนแปลงนั้นทำงานอยู่ ฉันเชื่อว่าวิธีการหลังถูกใช้โดยสิ่งต่าง ๆ เช่น Comparer <T> .Default ()
  2. สำหรับแต่ละคลาสที่น่าสนใจให้กำหนดคลาสหรือโครงสร้างที่ใช้ IGetWhethingClassInfo <T> และสร้างข้อ จำกัด "ใหม่" ชั้นจะไม่ประกอบด้วยเขตข้อมูลใด ๆ จริง ๆ แต่จะมีคุณสมบัติแบบคงที่ซึ่งคืนค่าเขตข้อมูลแบบคงที่ด้วยข้อมูลชนิด ส่งประเภทของคลาสหรือโครงสร้างนั้นไปยังชุดคำสั่งทั่วไปที่มีปัญหาซึ่งจะสามารถสร้างอินสแตนซ์และใช้เพื่อรับข้อมูลเกี่ยวกับคลาสอื่น หากคุณใช้คลาสเพื่อจุดประสงค์นี้คุณควรกำหนดคลาสแบบคงที่ตามที่ระบุไว้ด้านบนเพื่อหลีกเลี่ยงการสร้างอินสแตนซ์ตัวอธิบายวัตถุใหม่ทุกครั้ง หากคุณใช้ struct ค่าใช้จ่ายการเริ่มต้นควรเป็นศูนย์ แต่ทุกประเภทโครงสร้างที่แตกต่างกันจะต้องมีการขยายชุดคำสั่ง DoSomething

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


16

สายตาสั้นฉันเดา

เมื่อได้รับการออกแบบเริ่มต้นอินเทอร์เฟซตั้งใจให้ใช้กับอินสแตนซ์ของคลาสเท่านั้น

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

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

(ตอบกลับความคิดเห็น :) ฉันเชื่อว่าการเปลี่ยนแปลงตอนนี้จะต้องมีการเปลี่ยนแปลง CLR ซึ่งจะนำไปสู่ความไม่ลงรอยกันกับชุดประกอบที่มีอยู่


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

ฉันก็พบสิ่งนี้เช่นกันเมื่อใช้คลาสทั่วไปซึ่งต้องการประเภทพารามิเตอร์เพื่อสร้างตัวเองด้วยพารามิเตอร์บางตัว เนื่องจากใหม่ () ไม่สามารถรับได้ คุณคิดวิธีการทำยังไง Kramii?
Tom

1
@Kramii: สัญญาสำหรับ API แบบคงที่ ฉันไม่ต้องการอินสแตนซ์ของวัตถุรับประกันของลายเซ็นเฉพาะเช่น IMatrixMultiplier หรือ ICustomSerializer Funcs / Actions / Delegates ในฐานะสมาชิกชั้นเรียนทำเคล็ดลับ แต่บางครั้ง IMO นี้ดูเหมือนว่าเกินความเป็นจริงและอาจทำให้สับสนสำหรับผู้ที่ไม่มีประสบการณ์ในการพยายามขยาย API
David Cuccia

14

อินเทอร์เฟซระบุลักษณะการทำงานของวัตถุ

วิธีการคงที่ไม่ได้ระบุพฤติกรรมของวัตถุ แต่พฤติกรรมที่มีผลต่อวัตถุในบางวิธี


71
ขอโทษ .. ฉันไม่แน่ใจว่าถูกต้อง! อินเทอร์เฟซไม่ได้ระบุพฤติกรรม อินเตอร์เฟสกำหนดชุดของการดำเนินการที่ระบุชื่อ สองคลาสสามารถนำเมธอดของอินเตอร์เฟสไปใช้เพื่อทำงานในวิธีที่ต่างกันโดยสิ้นเชิง ดังนั้นอินเตอร์เฟสไม่ได้ระบุพฤติกรรมเลย คลาสที่ใช้งาน
Scott Langham

1
หวังว่าคุณจะไม่คิดว่าฉันเป็นคนจู้จี้จุกจิก .. แต่ฉันคิดว่ามันเป็นความแตกต่างที่สำคัญสำหรับทุกคนที่เรียนรู้ OO ที่จะเข้าใจ
Scott Langham

4
อินเทอร์เฟซควรระบุสัญญาที่มีลักษณะการทำงานและการนำเสนอ นั่นเป็นสาเหตุที่การเปลี่ยนพฤติกรรมของการโทรติดต่อเป็นแบบไม่ต้องดำเนินการทั้งคู่ หากคุณมีอินเทอร์เฟซที่การโทรทำหน้าที่แตกต่างกัน (เช่น IList.Add ทำการลบ) มันจะไม่ถูกต้อง
Jeff Yates

14
ใช่คุณจะถูกเหยเกในหัวเพื่อกำหนดวิธีการที่จะประพฤติไม่สอดคล้องกับชื่อของมัน หากคุณมี IAlertService.GetAssistance () แต่มันเป็นพฤติกรรมที่สามารถกะพริบแสงส่งเสียงเตือนหรือโผล่เข้ามาในดวงตาด้วยไม้เท้า
Scott Langham

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

14

ในขอบเขตที่อินเทอร์เฟซเป็นตัวแทนของ "สัญญา" ดูเหมือนว่าเงียบที่เหมาะสมสำหรับคลาสคงที่ในการใช้อินเทอร์เฟซ

ข้อโต้แย้งข้างต้นทั้งหมดดูเหมือนจะพลาดจุดนี้เกี่ยวกับสัญญา


2
ฉันเห็นด้วยกับคำตอบที่เรียบง่าย แต่มีประสิทธิภาพทั้งหมดนี้ สิ่งที่น่าสนใจใน "อินเตอร์เฟสคงที่" ก็คือมันจะเป็นตัวแทนของสัญญา อาจจะไม่ควรเรียกว่า "อินเตอร์เฟสแบบคงที่" แต่เรายังคงพลาดการสร้าง ตัวอย่างเช่นตรวจสอบเอกสารอย่างเป็นทางการของ. NET เกี่ยวกับส่วนต่อประสาน ICustomMarshaler มันต้องการคลาสที่ใช้มันเพื่อ "เพิ่มวิธีการคงที่ที่เรียกว่า GetInstance ที่ยอมรับสตริงเป็นพารามิเตอร์และมีชนิดส่งคืนของ ICustomMarshaler" ที่ไม่ดูอย่างจริงจังเช่น "อินเตอร์เฟซที่คงที่" ความหมายในภาษาอังกฤษธรรมดาในขณะที่ฉันต้องการมันใน C # ...
ไซมอน Mourier

@SimonMourier เอกสารนั้นสามารถเขียนได้ชัดเจนยิ่งขึ้น แต่คุณตีความผิดพลาด ไม่ใช่อินเทอร์เฟซ ICustomMarshaler ที่ต้องใช้วิธีคงที่ GetInstance มันคือแอตทริบิวต์รหัส [MarshalAs] ที่ต้องการสิ่งนี้ พวกเขาใช้รูปแบบจากโรงงานเพื่ออนุญาตให้แอททริบิวต์รับอินสแตนซ์ของ marshaler ที่แนบมา น่าเสียดายที่พวกเขาลืมที่จะรวมการทำเอกสารข้อกำหนดของ GetInstance ในหน้าเอกสารประกอบของ MarshalAs (แสดงเฉพาะตัวอย่างโดยใช้การติดตั้งในตัว)
Scott Gartner

@ScottGartner - ไม่ได้คะแนนของคุณ msdn.microsoft.com/en-us/library/ …ระบุอย่างชัดเจนว่า: "นอกเหนือจากการใช้อินเทอร์เฟซ ICustomMarshaler แล้ว marshalers แบบกำหนดเองต้องใช้วิธีการแบบคงที่ที่เรียกว่า GetInstance ที่ยอมรับสตริงเป็นพารามิเตอร์และมีชนิดคืนค่าของ ICustomMarshaler วิธีการคงที่ถูกเรียกโดย COM interop layer ของ runtime ภาษาทั่วไปเพื่อยกตัวอย่างอินสแตนซ์ของ marshaler ที่กำหนดเอง ". นี่เป็นคำนิยามสัญญาแบบคงที่แน่นอน
Simon Mourier

9

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

คุณจะเรียกมันว่าอย่างไร ??


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

1
ภาษาอื่น (เช่น Java) อนุญาตให้มีการเรียกใช้เมธอดสแตติกจากอินสแตนซ์ของวัตถุแม้ว่าคุณจะได้รับคำเตือนว่าคุณควรจะเรียกมันจากบริบทแบบสแตติก
Chris Marasti-Georg

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

1
@CharlesBretana ในกรณีของ generics ซึ่งเป็นประเภทที่ส่งผ่านฉันเห็นประโยชน์มากมายในการมีส่วนต่อประสานสำหรับวิธีการคงที่ (หรือถ้าคุณต้องการให้เรียกพวกเขาว่า "ส่วนต่อแบบคงที่" และอนุญาตให้ชั้นเรียนเพื่อกำหนดทั้งแบบคงที่ และอินเทอร์เฟซอินสแตนซ์) ดังนั้นถ้าผมมีWhatever<T>() where T:IMyStaticInterfaceผมสามารถเรียกWhatever<MyClass>()และมีT.MyStaticMethod()ภายในWhatever<T>()การดำเนินงานโดยไม่จำเป็นเช่น วิธีการโทรจะถูกกำหนดในรันไทม์ คุณสามารถทำได้ผ่านการไตร่ตรอง แต่ไม่มี "สัญญา" ที่จะถูกบังคับ
Jcl

4

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

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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

สิ่งที่ C # ต้องการคือประเภทข้อ จำกัด ที่แท้จริงอินเทอร์เฟซทั้งหมดก็จะเป็นข้อ จำกัด แต่ไม่ใช่ข้อ จำกัด ทั้งหมดที่จะเป็นอินเทอร์เฟซจากนั้นคุณสามารถทำสิ่งนี้ได้:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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


ตัวดำเนินการใน C # เป็นแบบสแตติกดังนั้นจึงยังไม่สมเหตุสมผล
จอห์นแซนเดอร์

จุดนั้นข้อ จำกัด จะตรวจสอบว่า TYPE (ไม่ใช่อินสแตนซ์) มีตัวดำเนินการ + (และโดยการขยายตัวดำเนินการ + =) และอนุญาตให้สร้างเทมเพลตจากนั้นเมื่อการแทนที่เทมเพลตจริงเกิดขึ้นวัตถุที่กำลังใช้ถูกรับประกันว่าเป็น ชนิดที่มีตัวดำเนินการนั้น (หรือวิธีการคงที่อื่น ๆ ) ดังนั้นคุณสามารถพูดได้: SumElements <int> (0, 5); ซึ่งจะยกตัวอย่างนี้: SumElements (int initVal, int [] ค่า) {foreach (var v ในค่า) {initVal + = v; }} ซึ่งแน่นอนว่าเหมาะสมแล้ว
Jeremy Sorensen

1
จำไว้ว่าไม่ใช่เทมเพลต เป็นข้อมูลทั่วไปซึ่งไม่เหมือนกับเท็มเพลต C ++
John Saunders

@JohnSaunders: Generics ไม่ใช่เทมเพลตและฉันไม่รู้ว่าพวกเขาจะสามารถทำงานกับตัวดำเนินการที่มีขอบเขตคงที่ได้อย่างไร แต่ในบริบททั่วไปมีหลายกรณีที่อาจเป็นประโยชน์ในการระบุว่า a Tควรมีสแตติกสมาชิก (เช่นโรงงานที่ผลิตTอินสแตนซ์) แม้จะไม่มีการเปลี่ยนแปลงรันไทม์ฉันคิดว่ามันเป็นไปได้ที่จะกำหนดวิธีการประชุมและผู้ช่วยที่จะช่วยให้ภาษาในการใช้สิ่งเช่นน้ำตาล syntactic ในรูปแบบที่มีประสิทธิภาพและทำงานร่วมกัน ถ้าสำหรับแต่ละอินเตอร์เฟสด้วยเมธอดสแตติกเสมือนมีคลาสตัวช่วย ...
supercat

1
@JohnSaunders: ปัญหาไม่ได้เป็นอย่างนั้นวิธีการที่ใช้อินเตอร์เฟซ แต่คอมไพเลอร์ไม่สามารถเลือกวิธีเสมือนในการโทรโดยไม่ต้องมีอินสแตนซ์วัตถุตามที่เลือกฐาน ทางออกคือการส่ง "การติดต่อคงที่" การโทรไม่ได้ใช้การส่งเสมือนจริง (ซึ่งจะไม่ทำงาน) แต่แทนที่จะใช้การโทรไปยังคลาสคงที่ทั่วไป การแจกจ่ายทั่วไปตามประเภทไม่จำเป็นต้องมีอินสแตนซ์ แต่มีเพียงประเภทเท่านั้น
supercat

3

เนื่องจากอินเทอร์เฟซอยู่ในโครงสร้างการสืบทอดและวิธีการแบบคงที่ไม่สืบทอดดี


3

สิ่งที่คุณต้องการจะอนุญาตให้มีการเรียกใช้เมธอดสแตติกผ่านทางชนิดหรืออินสแตนซ์ใด ๆ ของประเภทนั้น อย่างน้อยที่สุดก็จะส่งผลให้เกิดความกำกวมซึ่งไม่ใช่ลักษณะที่ต้องการ

จะมีการถกเถียงกันไม่สิ้นสุดว่ามันสำคัญหรือไม่ซึ่งเป็นแนวปฏิบัติที่ดีที่สุดและมีปัญหาด้านประสิทธิภาพหรือไม่ไม่ทางใดก็ทางหนึ่ง โดยไม่สนับสนุน C # ช่วยให้เราไม่ต้องกังวลกับมัน

อาจเป็นไปได้ว่าคอมไพเลอร์ที่สอดคล้องกับความปรารถนานี้จะสูญเสียการเพิ่มประสิทธิภาพบางอย่างที่อาจมาพร้อมกับการแยกที่เข้มงวดมากขึ้นระหว่างอินสแตนซ์และวิธีการคงที่


น่าสนใจเป็นไปได้มากที่จะเรียกใช้เมธอดแบบคงที่โดยทั้งสองประเภทโฆษณาอินสแตนซ์ใน VB.Net (แม้ว่า IDE จะแจ้งเตือนในกรณีหลัง) ดูเหมือนจะไม่เป็นปัญหา คุณอาจถูกต้องเกี่ยวกับการเพิ่มประสิทธิภาพ
Kramii

3

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


นี่เป็นมุมมองที่น่าสนใจและน่าจะเป็นสิ่งที่นักออกแบบ C # นึกไว้ ฉันจะคิดว่าสมาชิกคงที่ในวิธีที่แตกต่างจากนี้ไป
Kramii

3

เพื่อยกตัวอย่างที่ฉันขาดการนำวิธีการอินเตอร์เฟสไปใช้หรือสิ่งที่ Mark Brackett แนะนำให้ใช้เป็น "วิธีการพิมพ์ที่เรียกว่า":

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

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

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


1

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


1

การเชื่อมต่อเป็นชุดนามธรรมของฟังก์ชั่นที่มีอยู่ที่กำหนดไว้

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

ถ้าเมธอดถูกกำหนดเป็นสแตติกคลาสที่ใช้อินเตอร์เฟสจะไม่ถูกห่อหุ้มเท่าที่ควร การห่อหุ้มเป็นสิ่งที่ดีในการมุ่งเน้นในการออกแบบเชิงวัตถุ (ฉันจะไม่เข้าไปหาสาเหตุคุณสามารถอ่านได้ที่นี่: http://en.wikipedia.org/wiki/Object-oriented ) ด้วยเหตุผลนี้จึงไม่อนุญาตให้ใช้วิธีการคงที่ในส่วนต่อประสาน


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

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

True Scott แต่ไม่อนุญาตให้บุคคลอื่นเข้าถึงวิธีนั้นในลักษณะคงที่ในส่วนอื่นของรหัส ไม่ใช่ว่าฉันสามารถนึกถึงเหตุผลที่คุณต้องการ แต่ดูเหมือนจะเป็นสิ่งที่ OP ขอ
Chris Marasti-Georg

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

1

คลาสที่คงที่ควรทำสิ่งนี้เพื่อให้สามารถใช้งานได้ทั่วไป ฉันต้องใช้ Singleton แทนเพื่อให้ได้ผลลัพธ์ที่ต้องการ

ฉันมีคลาสของชั้นธุรกิจแบบคงที่ที่ใช้วิธีการ CRUD เช่น "สร้าง", "อ่าน", "อัปเดต", "ลบ" สำหรับแต่ละประเภทเอนทิตีเช่น "ผู้ใช้", "ทีม" ฯลฯ จากนั้นฉันสร้างฐาน ควบคุมที่มีคุณสมบัตินามธรรมสำหรับชั้นธุรกิจที่ใช้วิธีการ CRUD สิ่งนี้ทำให้ฉันสามารถดำเนินการ "สร้าง", "อ่าน", "อัปเดต", "ลบ" โดยอัตโนมัติจากคลาสฐาน ฉันต้องใช้ซิงเกิลตันเนื่องจากข้อ จำกัด คงที่


1

คนส่วนใหญ่ดูเหมือนจะลืมว่าใน OOP Classes เป็นวัตถุด้วยและพวกเขาก็มีข้อความซึ่งด้วยเหตุผลบางอย่าง c # เรียกว่า "วิธีการคงที่" ความจริงที่ว่ามีความแตกต่างระหว่างวัตถุอินสแตนซ์และวัตถุคลาสแสดงข้อบกพร่องหรือข้อบกพร่องในภาษาเท่านั้น Optimist เกี่ยวกับ c # แม้ว่า ...


2
ปัญหาคือว่าคำถามนี้ไม่เกี่ยวกับ "ใน OOP" มันเกี่ยวกับ "ใน C #" ใน C # ไม่มี "ข้อความ"
John Saunders

1

ตกลงที่นี่เป็นตัวอย่างของความต้องการ 'วิธีการพิมพ์' ฉันกำลังสร้างหนึ่งในชุดของชั้นเรียนตาม XML แหล่งที่มาบางส่วน ดังนั้นฉันมี

  static public bool IsHandled(XElement xml)

ฟังก์ชั่นซึ่งเรียกว่าเปิดในแต่ละชั้นเรียน

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

มันเป็นการดีที่จะเพิ่มไปยังอินเทอร์เฟซเพื่อบังคับให้ตัวเรียกคลาสใช้งาน นี่จะไม่ทำให้เกิดค่าใช้จ่ายที่สำคัญ - มันเป็นเพียงการตรวจสอบรวบรวม / ลิงค์เวลาและไม่ส่งผลกระทบต่อ vtable

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

ดังนั้นมันจึงเป็นคุณสมบัติที่อาจเกิดขึ้นได้เล็กน้อยซึ่งความสมดุลนั้นน่าจะดีที่สุด


1

ความจริงที่ว่าคลาสแบบสแตติกถูกนำมาใช้ใน C # โดย Microsoft การสร้างอินสแตนซ์พิเศษของคลาสที่มีองค์ประกอบแบบสแตติกนั้นเป็นเพียงความผิดปกติของวิธีการใช้งานสแตติก มันไม่ได้เป็นประเด็นทางทฤษฎี

อินเทอร์เฟซที่ควรเป็นตัวบ่งชี้ของอินเทอร์เฟซของคลาส - หรือวิธีการโต้ตอบกับและควรมีการโต้ตอบที่คงที่ คำจำกัดความทั่วไปของอินเทอร์เฟซ (จาก Meriam-Webster): สถานที่หรือพื้นที่ที่สิ่งต่าง ๆ พบและสื่อสารกับหรือส่งผลกระทบต่อกันและกัน เมื่อคุณละเว้นส่วนประกอบสแตติกของคลาสหรือคลาสสแตติกทั้งหมดเราจะไม่สนใจส่วนใหญ่ของการที่ชายเลวเหล่านี้โต้ตอบ

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

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

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


นี่เป็นคำถามโบราณ วันนี้เราพยายามอย่างหนักที่จะหลีกเลี่ยงคำถามตามความคิดเห็นเพราะยากที่จะตอบคำถามเชิงอนุญาต วิธีเดียวที่จะตอบคำถามนี้ได้ดีก็คืออธิบายถึงเหตุผลที่ MS มีหรือควรมีเหตุผลที่ทำได้
นาธาน Tuggy

1

C # และ CLR ควรสนับสนุนวิธีการคงที่ในส่วนต่อประสานเช่นเดียวกับ Java ตัวแก้ไขแบบสแตติกเป็นส่วนหนึ่งของคำนิยามสัญญาและมีความหมายโดยเฉพาะอย่างยิ่งพฤติกรรมและค่าส่งคืนนั้นไม่ได้แตกต่างกันไปตามอินสแตนซ์แม้ว่ามันอาจจะยังคงแตกต่างจากการโทรถึงโทร

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


0

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


0

ฉันคิดว่าคำถามกำลังได้รับจากข้อเท็จจริงที่ว่า C # ต้องการคำหลักอื่นสำหรับสถานการณ์เช่นนี้อย่างแม่นยำ คุณต้องการวิธีที่ค่าส่งคืนขึ้นอยู่กับชนิดที่มันถูกเรียก คุณไม่สามารถเรียกมันว่า "คงที่" หากไม่รู้จักประเภทดังกล่าว แต่เมื่อทราบชนิดแล้วจะกลายเป็นแบบคงที่ "Unresolved Static" เป็นแนวคิด - มันยังไม่คงที่ แต่เมื่อเราทราบชนิดที่ได้รับแล้วจะเป็น นี่เป็นแนวคิดที่ดีอย่างสมบูรณ์แบบซึ่งเป็นเหตุผลที่โปรแกรมเมอร์ขอต่อไป แต่มันก็ไม่สอดคล้องกับวิธีที่นักออกแบบคิดเกี่ยวกับภาษา

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

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

ตามแนวคิดเชิงวัตถุอินเทอร์เฟซที่ดำเนินการโดยชั้นเรียนและมีสัญญาในการเข้าถึงฟังก์ชั่นการใช้งานเหล่านี้ (หรือวิธีการ) โดยใช้วัตถุ

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


0

แนวคิดไม่มีเหตุผลว่าทำไมอินเตอร์เฟสไม่สามารถกำหนดสัญญาที่มีวิธีการแบบคงที่ได้

สำหรับการใช้งานภาษา C # ในปัจจุบันข้อ จำกัด นั้นเกิดจากค่าเผื่อการสืบทอดของคลาสฐานและอินเตอร์เฟส ถ้า "คลาส SomeBaseClass" ใช้ "อินเตอร์เฟส ISomeInterface" และ "คลาส SomeDerivedClass: SomeBaseClass, ISomeInterface" ยังใช้อินเทอร์เฟซวิธีคงที่ในการใช้วิธีการอินเทอร์เฟซจะล้มเหลวเนื่องจากวิธีการแบบคงที่ไม่สามารถมีลายเซ็นเดียวกัน อยู่ในคลาสฐานเพื่อใช้อินเตอร์เฟส)

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

ดังนั้นมันจึงลดลงไปจนถึงข้อ จำกัด ของความขัดแย้งของชื่อ C # เช่นและวิธีการคงที่ของชื่อเดียวกันในการสืบทอด ไม่มีเหตุผลที่ C # ไม่สามารถ "อัปเกรด" เพื่อรองรับสัญญาวิธีคงที่ (อินเทอร์เฟซ)


-1

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

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