เหตุใดอินเตอร์เฟส C # จึงไม่มีฟิลด์


223

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


21
อินเทอร์เฟซไม่มีการนำไปใช้สำหรับการใช้คลาสนามธรรมกับคุณสมบัติปี
PostMan

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

5
แต่ถ้าเป็นสนามสาธารณะมันเป็นส่วนหนึ่งของสัญญาและไม่เพียง แต่รายละเอียดการติดตั้งใช่มั้ย
อเล็กซ์

คำตอบ:


238

แม้ว่าคำตอบอื่น ๆ อีกมากมายนั้นถูกต้องในระดับความหมาย แต่ฉันคิดว่ามันน่าสนใจที่จะเข้าหาคำถามประเภทนี้จากระดับรายละเอียดการใช้งาน

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

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

ชั้นพูดว่า "เมื่อคุณสร้างตัวอย่างของฉันอ้างอิงถึง Foo.M ในช่องสำหรับ IFoo.M

จากนั้นเมื่อคุณโทรออก:

IFoo ifoo = new Foo();
ifoo.M();

คอมไพเลอร์สร้างรหัสที่ระบุว่า "ถามวัตถุว่ามีวิธีใดบ้างในช่องสำหรับ IFoo.M และเรียกวิธีนั้น

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


26
สิ่งหนึ่งที่ฉันทำพลาดบางครั้งก็คือความสามารถในการคล้ายจาวาในการกำหนดค่าคงที่ระดับอินเตอร์เฟสซึ่งสันนิษฐานว่าคงไม่ต้องใช้ "สล็อต" เพื่อรองรับภาษา
LBushkin

2
ฉันชอบคำอธิบายในคำง่าย ๆ ขอบคุณ "CLR ผ่าน C #" และ "Essential .net volume 1" ให้รายละเอียดเพิ่มเติม
Sandeep GB

6
เหตุใดเขตข้อมูลจึงไม่มีสล็อต และคำถามเดียวกันกับผู้ประกอบการ ฉันจำได้ว่าได้ยินเกี่ยวกับการพิมพ์เป็ดโดยใช้การสะท้อนเพื่อดูว่ามีการใช้งานอินเทอร์เฟซแม้ว่าคลาสไม่ได้รับอินเทอร์เฟซ เหตุใดจึงไม่ใช้การสะท้อน (หรือช่อง) ในการดึงฟิลด์ ฉันยังคงเขียนรหัสของฉันดังนั้นฉันอาจไม่ต้องการ / ต้องการสาขา แต่ฉันแปลกใจที่พบว่าฉันไม่สามารถใช้โอเปอเรเตอร์ได้ ตัวดำเนินการเหมือนกับวิธีการจากความเข้าใจของฉันยกเว้นไม่ทั้งหมดสามารถโอเวอร์โหลด ( An interface cannot contain constants, fields, operatorsจากmsdn.microsoft.com/en-us/library/ms173156.aspx )

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

1
@ b1nary.atr0phy ทำไมถึงมีฟิลด์ เช่นถ้าฉันประกาศวิธีint One()การใช้งานpublic int One(){return 1;}นั้นไม่ใช่เขตข้อมูล
Hi-Angel

131

อินเทอร์เฟซใน C # มีวัตถุประสงค์เพื่อกำหนดสัญญาที่ชั้นจะปฏิบัติตาม - ไม่ใช้งานเฉพาะ

ด้วยจิตวิญญาณนั้นอินเทอร์เฟซของ C # จะอนุญาตให้มีการกำหนดคุณสมบัติซึ่งผู้โทรต้องจัดหาการปรับใช้สำหรับ

interface ICar
{
    int Year { get; set; }
}

การใช้คลาสสามารถใช้คุณสมบัติอัตโนมัติเพื่อทำให้การติดตั้งง่ายขึ้นหากไม่มีตรรกะพิเศษที่เกี่ยวข้องกับคุณสมบัติ:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}

5
ไม่ใช่ทุกสิ่งที่เป็นสาธารณะซึ่งเป็นส่วนหนึ่งของสัญญา หากชั้นเรียนมีปีที่เป็นสาธารณะไม่ได้หมายความว่าจะมีสัญญาประเภทชั้นเรียนที่มีประเภทปีที่จะเข้าร่วมและสามารถเข้าถึงได้?
Didier A.

1
สายไปงานเลี้ยง แต่ไม่ในกรณีนี้มันหมายถึงสัญญามีปีทรัพย์สินที่ชั้นปฏิบัติใด ๆ ที่ควรจะดำเนินการ คุณสมบัติเป็นวิธีรับ / ตั้งค่าจริง ๆ ซึ่งมีเขตข้อมูลสำรองสร้างขึ้นโดยอัตโนมัติหากไม่จำเป็นต้องใช้ตรรกะพิเศษ ไวยากรณ์พิเศษมีไว้สำหรับสัญกรณ์ที่ชัดเจนยิ่งขึ้น
user3613916

ฉันจะกำหนดค่าเริ่มต้นคงที่ (เช่น 123) ให้กับการใช้งานอัตโนมัตินั้นได้Yearอย่างไร
lama12345

1
@ lama12345 ฉันมาช้าไปงานปาร์ตี้ แต่เนื่องจาก C # 6 (2015, .NET Framework 4.6 และ. NET Core) คุณสามารถใช้คุณสมบัติอัตโนมัติเพื่อจุดประสงค์นั้นได้ public int Year => 123;. อย่างไรก็ตามในกรณีนี้มันไม่มีเหตุผลที่จะมี setter ดังนั้นอินเตอร์เฟสจะต้องถูกกำหนดด้วยint Year { get; }
EriF89

56

ประกาศเป็นคุณสมบัติ:

interface ICar {
   int Year { get; set; }
}

47
คำถามคือ " ทำไม C # อินเตอร์เฟสไม่สามารถมีฟิลด์ได้?" นี่ไม่ใช่ที่อยู่ที่
AakashM

14
ฉันยอมรับว่าคำตอบนี้ไม่ได้ตอบคำถาม OP แต่ก็แก้ปัญหาของฉันได้
Gorgen

36

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

คำสำคัญเสมือนถูกนำมาใช้ใน. NET ด้วยวิธีการและที่เรียกว่า v-table, อาเรย์ของตัวชี้วิธี คีย์เวิร์ด override จะเติมช่อง v-table ด้วยตัวชี้เมธอดที่แตกต่างกันเขียนทับช่องที่สร้างโดยคลาสพื้นฐาน คุณสมบัติเหตุการณ์และตัวทำดัชนีถูกใช้เป็นวิธีการภายใต้ประทุน แต่ทุ่งนาไม่ใช่ การเชื่อมต่อจึงไม่สามารถมีเขตข้อมูลได้


คุณให้ชื่อทางเทคนิค / จริง (v-table) กับแนวคิดของช่องที่เอริคพูดถึง ขอบคุณสำหรับรายละเอียดฮันส์
RBT

จุด v-table จะเป็นอย่างไรหากอินเตอร์เฟสไม่ได้รับอนุญาตให้ใช้งานเริ่มต้น การเปลี่ยนแปลงนี้ใน C # 8.0 แต่นั่นอยู่ด้านข้าง
AnthonyMonterrosa

19

ทำไมไม่เพียงแค่มีYearทรัพย์สินซึ่งดีอย่างสมบูรณ์?

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

ตัวอย่างเช่นส่วนหนึ่งของข้อมูลYearจำเพาะของคุณอาจกำหนดให้ไม่ถูกต้องสำหรับผู้ICarดำเนินการเพื่ออนุญาตการมอบหมายให้Yearซึ่งช้ากว่าปีปัจจุบัน +1 หรือก่อนปี 1900 ไม่มีทางที่จะพูดได้ว่าถ้าคุณได้สัมผัสYearทุ่งนา - ดีกว่าที่จะใช้ คุณสมบัติแทนการทำงานที่นี่


18

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

พิจารณาสถานการณ์นี้โดยใช้สิ่งที่คุณแนะนำ:

public interface InterfaceOne
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public interface InterfaceTwo
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public class MyClass : InterfaceOne, InterfaceTwo { }

เรามีปัญหาสองสามข้อที่นี่:

  • เนื่องจากสมาชิกทั้งหมดของอินเทอร์เฟซเป็นแบบสาธารณะคำนิยามตัวแปรสำรองของเราจึงถูกเปิดเผยต่อผู้ใช้อินเทอร์เฟซ
  • ซึ่งmyBackingVariableจะMyClassใช้งานหรือไม่

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

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

7

คนอื่นให้ 'ทำไม' ดังนั้นฉันจะเพิ่มว่าอินเทอร์เฟซของคุณสามารถกำหนดตัวควบคุม ถ้าคุณห่อในทรัพย์สิน:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}

3

อินเทอร์เฟซไม่มีการใช้งานใด ๆ

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

3

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

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


1
หากคุณต้องการเข้าถึงสมาชิกเพียงแค่ทำให้เป็นทรัพย์สินและคุณจะดี
Unome

0

สำหรับสิ่งนี้คุณสามารถมีคลาสฐานรถที่ใช้ฟิลด์ปีและการนำไปใช้งานอื่น ๆ ทั้งหมดสามารถสืบทอดได้


0

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

ตามที่ระบุโดยการตอบกลับอื่น ๆ คุณสามารถกำหนดคลาสพื้นฐานและกำหนดคุณสมบัติที่มีการป้องกันซึ่งผู้สืบทอดทั้งหมดสามารถเข้าถึงได้

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

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