ตัวอย่างเช่นสมมติว่าฉันต้องการอินเตอร์เฟซและการใช้งานทั้งหมดจะประกอบด้วยสนามICar
Year
นี่หมายความว่าทุกการใช้งานจะต้องประกาศแยกกันYear
หรือไม่ จะดีกว่าหรือเปล่าที่จะกำหนดสิ่งนี้ในอินเทอร์เฟซ
ตัวอย่างเช่นสมมติว่าฉันต้องการอินเตอร์เฟซและการใช้งานทั้งหมดจะประกอบด้วยสนามICar
Year
นี่หมายความว่าทุกการใช้งานจะต้องประกาศแยกกันYear
หรือไม่ จะดีกว่าหรือเปล่าที่จะกำหนดสิ่งนี้ในอินเทอร์เฟซ
คำตอบ:
แม้ว่าคำตอบอื่น ๆ อีกมากมายนั้นถูกต้องในระดับความหมาย แต่ฉันคิดว่ามันน่าสนใจที่จะเข้าหาคำถามประเภทนี้จากระดับรายละเอียดการใช้งาน
อินเตอร์เฟซที่สามารถจะคิดว่าเป็นคอลเลกชันของช่องซึ่งมีวิธีการ เมื่อคลาสใช้อินเทอร์เฟซจำเป็นต้องมีคลาสเพื่อบอกรันไทม์ถึงวิธีการกรอกข้อมูลลงในสล็อตที่จำเป็นทั้งหมด เมื่อคุณพูด
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 ของเหตุการณ์ แต่ข้อมูลไม่ได้เป็นวิธีการที่ ไม่มี "ช่องเสียบ" ที่เชื่อมโยงกับช่องที่คุณสามารถ "กรอก" พร้อมกับอ้างอิงถึงตำแหน่งของช่อง ดังนั้นอินเทอร์เฟซสามารถกำหนดวิธีการคุณสมบัติตัวสร้างดัชนีและเหตุการณ์ แต่ไม่ใช่เขตข้อมูล
An interface cannot contain constants, fields, operators
จากmsdn.microsoft.com/en-us/library/ms173156.aspx )
int One()
การใช้งานpublic int One(){return 1;}
นั้นไม่ใช่เขตข้อมูล
อินเทอร์เฟซใน C # มีวัตถุประสงค์เพื่อกำหนดสัญญาที่ชั้นจะปฏิบัติตาม - ไม่ใช้งานเฉพาะ
ด้วยจิตวิญญาณนั้นอินเทอร์เฟซของ C # จะอนุญาตให้มีการกำหนดคุณสมบัติซึ่งผู้โทรต้องจัดหาการปรับใช้สำหรับ
interface ICar
{
int Year { get; set; }
}
การใช้คลาสสามารถใช้คุณสมบัติอัตโนมัติเพื่อทำให้การติดตั้งง่ายขึ้นหากไม่มีตรรกะพิเศษที่เกี่ยวข้องกับคุณสมบัติ:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year
อย่างไร
public int Year => 123;
. อย่างไรก็ตามในกรณีนี้มันไม่มีเหตุผลที่จะมี setter ดังนั้นอินเตอร์เฟสจะต้องถูกกำหนดด้วยint Year { get; }
Eric Lippert จับมันฉันจะใช้วิธีอื่นในการพูดในสิ่งที่เขาพูด สมาชิกทั้งหมดของอินเทอร์เฟซเสมือนและพวกเขาทั้งหมดต้องถูกแทนที่โดยคลาสที่สืบทอดอินเทอร์เฟซ คุณไม่ได้เขียนคำหลักเสมือนอย่างชัดเจนในการประกาศอินเทอร์เฟซหรือใช้คำหลักแทนที่ในชั้นเรียนพวกเขามีความหมายโดยนัย
คำสำคัญเสมือนถูกนำมาใช้ใน. NET ด้วยวิธีการและที่เรียกว่า v-table, อาเรย์ของตัวชี้วิธี คีย์เวิร์ด override จะเติมช่อง v-table ด้วยตัวชี้เมธอดที่แตกต่างกันเขียนทับช่องที่สร้างโดยคลาสพื้นฐาน คุณสมบัติเหตุการณ์และตัวทำดัชนีถูกใช้เป็นวิธีการภายใต้ประทุน แต่ทุ่งนาไม่ใช่ การเชื่อมต่อจึงไม่สามารถมีเขตข้อมูลได้
ทำไมไม่เพียงแค่มีYear
ทรัพย์สินซึ่งดีอย่างสมบูรณ์?
อินเทอร์เฟซไม่ประกอบด้วยเขตข้อมูลเนื่องจากเขตข้อมูลแสดงการใช้งานที่เฉพาะเจาะจงของการแสดงข้อมูลและการเปิดเผยเหล่านั้นจะแบ่ง encapsulation ดังนั้นการมีส่วนต่อประสานกับเขตข้อมูลอย่างมีประสิทธิภาพจะถูกกำหนดให้นำไปปฏิบัติแทนที่จะเป็นส่วนต่อประสานซึ่งเป็นความขัดแย้งที่แปลกประหลาดสำหรับส่วนต่อประสานที่มี!
ตัวอย่างเช่นส่วนหนึ่งของข้อมูลYear
จำเพาะของคุณอาจกำหนดให้ไม่ถูกต้องสำหรับผู้ICar
ดำเนินการเพื่ออนุญาตการมอบหมายให้Year
ซึ่งช้ากว่าปีปัจจุบัน +1 หรือก่อนปี 1900 ไม่มีทางที่จะพูดได้ว่าถ้าคุณได้สัมผัสYear
ทุ่งนา - ดีกว่าที่จะใช้ คุณสมบัติแทนการทำงานที่นี่
คำตอบสั้น ๆ คือใช่ทุกประเภทการนำไปใช้จะต้องสร้างตัวแปรสำรองของตัวเอง นี่เป็นเพราะอินเทอร์เฟซคล้ายคลึงกับสัญญา สิ่งที่สามารถทำได้คือระบุชิ้นส่วนของรหัสที่สาธารณชนสามารถเข้าถึงได้ที่ประเภทการนำไปใช้งานต้องพร้อมใช้งาน มันไม่สามารถมีรหัสใด ๆ
พิจารณาสถานการณ์นี้โดยใช้สิ่งที่คุณแนะนำ:
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; }
}
}
คนอื่นให้ 'ทำไม' ดังนั้นฉันจะเพิ่มว่าอินเทอร์เฟซของคุณสามารถกำหนดตัวควบคุม ถ้าคุณห่อในทรัพย์สิน:
public interface IView {
Control Year { get; }
}
public Form : IView {
public Control Year { get { return uxYear; } } //numeric text box or whatever
}
อินเทอร์เฟซไม่มีการใช้งานใด ๆ
มีการพูดกันมากมายแล้ว แต่เพื่อให้ง่ายนี่คือความคิดของฉัน อินเทอร์เฟซมีไว้เพื่อให้สัญญาวิธีการที่จะใช้งานโดยผู้บริโภคหรือคลาสและไม่มีเขตข้อมูลสำหรับเก็บค่า
คุณอาจโต้แย้งว่าทำไมคุณสมบัติจึงได้รับอนุญาต ดังนั้นคำตอบง่ายๆคือ - คุณสมบัติถูกกำหนดไว้ภายในเป็นวิธีการเท่านั้น
สำหรับสิ่งนี้คุณสามารถมีคลาสฐานรถที่ใช้ฟิลด์ปีและการนำไปใช้งานอื่น ๆ ทั้งหมดสามารถสืบทอดได้
อินเทอร์เฟซกำหนดคุณสมบัติของอินสแตนซ์สาธารณะและวิธีการ โดยทั่วไปแล้วฟิลด์จะเป็นแบบส่วนตัวหรือภายในที่มีการป้องกันมากที่สุดภายในหรือที่มีการป้องกัน
ตามที่ระบุโดยการตอบกลับอื่น ๆ คุณสามารถกำหนดคลาสพื้นฐานและกำหนดคุณสมบัติที่มีการป้องกันซึ่งผู้สืบทอดทั้งหมดสามารถเข้าถึงได้
สิ่งหนึ่งที่แปลกคืออินเทอร์เฟซสามารถถูกกำหนดให้เป็นภายในแต่มัน จำกัด ประโยชน์ของอินเทอร์เฟซและโดยทั่วไปจะใช้เพื่อกำหนดฟังก์ชันการทำงานภายในที่ไม่ได้ใช้โดยรหัสภายนอกอื่น ๆ