ดังนั้นกรณีการใช้งานที่ดีสำหรับการใช้งานอินเทอร์เฟซอย่างชัดเจนคืออะไร?
เป็นเพียงเพื่อให้คนที่ใช้คลาสไม่ต้องดูวิธีการ / คุณสมบัติทั้งหมดใน Intellisense?
ดังนั้นกรณีการใช้งานที่ดีสำหรับการใช้งานอินเทอร์เฟซอย่างชัดเจนคืออะไร?
เป็นเพียงเพื่อให้คนที่ใช้คลาสไม่ต้องดูวิธีการ / คุณสมบัติทั้งหมดใน Intellisense?
คำตอบ:
หากคุณใช้สองอินเทอร์เฟซทั้งด้วยวิธีการเดียวกันและการใช้งานที่แตกต่างกันคุณจะต้องนำไปใช้อย่างชัดเจน
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
การซ่อนสมาชิกที่ไม่ต้องการนั้นมีประโยชน์ ตัวอย่างเช่นหากคุณใช้ทั้งสองอย่างIComparable<T>
และIComparable
มักจะดีกว่าในการซ่อนการIComparable
โอเวอร์โหลดเพื่อไม่ให้ผู้คนรู้สึกว่าคุณสามารถเปรียบเทียบวัตถุประเภทต่างๆได้ ในทำนองเดียวกันอินเทอร์เฟซบางอย่างไม่สอดคล้องกับ CLS IConvertible
ดังนั้นหากคุณไม่ใช้อินเทอร์เฟซอย่างชัดเจนผู้ใช้ปลายทางของภาษาที่ต้องปฏิบัติตามข้อกำหนด CLS จะไม่สามารถใช้ออบเจ็กต์ของคุณได้ (ซึ่งจะเป็นหายนะมากถ้าผู้ใช้ BCL ไม่ได้ซ่อนสมาชิก IConvertible ของดั้งเดิม :))
ข้อสังเกตที่น่าสนใจอีกประการหนึ่งคือโดยปกติการใช้โครงสร้างดังกล่าวหมายความว่าโครงสร้างที่ใช้อินเทอร์เฟซอย่างชัดเจนสามารถเรียกใช้โดยการชกมวยกับประเภทอินเทอร์เฟซเท่านั้น คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยใช้ข้อ จำกัด ทั่วไป ::
void SomeMethod<T>(T obj) where T:IConvertible
จะไม่ใส่กล่อง int เมื่อคุณส่งผ่านไป
string
อยู่รอบ ๆ ก่อนชื่อสามัญและการปฏิบัตินี้อยู่ในสมัย เมื่อ. net 2 เข้ามาพวกเขาไม่ต้องการทำลายอินเทอร์เฟซสาธารณะstring
ดังนั้นพวกเขาจึงทิ้งมันไว้เหมือนเดิมโดยมีระบบป้องกันอยู่
เหตุผลเพิ่มเติมบางประการในการติดตั้งอินเทอร์เฟซอย่างชัดเจน:
ความเข้ากันได้แบบย้อนกลับ : ในกรณีที่ICloneable
อินเทอร์เฟซเปลี่ยนไปการนำสมาชิกคลาสเมธอดไปใช้ไม่จำเป็นต้องเปลี่ยนลายเซ็นของเมธอด
รหัสทำความสะอาด : จะมีข้อผิดพลาดของคอมไพเลอร์หากClone
วิธีการถูกลบออกจาก ICloneable อย่างไรก็ตามหากคุณใช้วิธีนี้โดยปริยายคุณสามารถจบลงด้วยเมธอดสาธารณะ 'orphaned' ที่ไม่ได้ใช้
การพิมพ์ที่ชัดเจน : เพื่อแสดงเรื่องราวของ supercat ด้วยตัวอย่างนี่จะเป็นโค้ดตัวอย่างที่ฉันต้องการการใช้งานICloneable
อย่างชัดเจนอนุญาตให้Clone()
พิมพ์อย่างชัดเจนเมื่อคุณเรียกมันโดยตรงว่าเป็นMyObject
สมาชิกอินสแตนซ์:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
interface ICloneable<out T> { T Clone(); T self {get;} }
เพื่อที่หนึ่งฉันฉันไม่ต้องการ โปรดทราบว่าไม่มีICloneable<T>
ข้อ จำกัดใด ๆใน T. ในขณะที่โดยทั่วไปวัตถุสามารถโคลนได้อย่างปลอดภัยหากฐานของมันสามารถเป็นได้ แต่ก็อาจต้องการได้มาจากคลาสฐานซึ่งสามารถโคลนวัตถุของคลาสที่ไม่สามารถทำได้อย่างปลอดภัย เพื่อให้เป็นเช่นนั้นฉันไม่แนะนำให้คลาสที่สืบทอดได้เปิดเผยวิธีการโคลนสาธารณะ มีคลาสที่สืบทอดได้แทนด้วยprotected
วิธีการโคลนนิ่งและคลาสที่ปิดผนึกซึ่งได้มาจากคลาสเหล่านั้นและเปิดเผยการโคลนนิ่งสาธารณะ
อีกเทคนิคหนึ่งที่มีประโยชน์คือการใช้วิธีการแบบสาธารณะของฟังก์ชันจะส่งคืนค่าที่เฉพาะเจาะจงมากกว่าที่ระบุไว้ในอินเทอร์เฟซ
ตัวอย่างเช่นออบเจ็กต์สามารถใช้งานICloneable
ได้ แต่ยังคงมีClone
เมธอดที่เปิดเผยต่อสาธารณะส่งคืนประเภทของมันเอง
ในทำนองเดียวกันIAutomobileFactory
อาจมีManufacture
เมธอดที่ส่งคืน an Automobile
แต่ a FordExplorerFactory
ซึ่งใช้งานIAutomobileFactory
อาจมีManufacture
วิธีการส่งคืน a FordExplorer
(ซึ่งมาจากAutomobile
) รหัสที่รู้ว่าFordExplorerFactory
สามารถใช้ - FordExplorer
คุณสมบัติเฉพาะบนวัตถุที่ส่งคืนโดยFordExplorerFactory
ไม่ต้องพิมพ์แคสต์ในขณะที่รหัสที่รู้เพียงว่ามันมีบางประเภทIAutomobileFactory
ก็จะจัดการกับการกลับมาเป็นAutomobile
ไฟล์.
นอกจากนี้ยังมีประโยชน์เมื่อคุณมีสองอินเทอร์เฟซที่มีชื่อสมาชิกและลายเซ็นเดียวกัน แต่ต้องการเปลี่ยนลักษณะการทำงานขึ้นอยู่กับวิธีการใช้งาน (ฉันไม่แนะนำให้เขียนโค้ดแบบนี้):
interface Cat
{
string Name {get;}
}
interface Dog
{
string Name{get;}
}
public class Animal : Cat, Dog
{
string Cat.Name
{
get
{
return "Cat";
}
}
string Dog.Name
{
get
{
return "Dog";
}
}
}
static void Main(string[] args)
{
Animal animal = new Animal();
Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
Dog dog = animal;
Console.WriteLine(cat.Name); //Prints Cat
Console.WriteLine(dog.Name); //Prints Dog
}
public class Animal : Cat, Dog
สามารถทำให้อินเทอร์เฟซสาธารณะสะอาดขึ้นเพื่อใช้อินเทอร์เฟซอย่างชัดเจนกล่าวคือFile
คลาสของคุณอาจนำไปใช้IDisposable
อย่างชัดเจนและให้วิธีการสาธารณะClose()
ซึ่งอาจเหมาะสมกับผู้บริโภคมากกว่าDispose(
)
F # นำเสนอการใช้งานอินเทอร์เฟซที่ชัดเจนเท่านั้นดังนั้นคุณจึงต้องแคสต์ไปยังอินเทอร์เฟซเฉพาะเพื่อเข้าถึงฟังก์ชันการทำงานซึ่งทำให้มีการใช้อินเทอร์เฟซที่ชัดเจนมาก (ไม่มีการเล่นสำนวน)
Dispose
คือสิ่งที่ไม่ต้องการการล้างข้อมูล) IList<T>.Add
เป็นตัวอย่างที่ดีกว่าจะเป็นสิ่งที่ชอบการดำเนินงานคอลเลกชันที่ไม่เปลี่ยนรูปของ
หากคุณมีอินเทอร์เฟซภายในและคุณไม่ต้องการใช้สมาชิกในชั้นเรียนของคุณแบบสาธารณะคุณจะต้องนำไปใช้อย่างชัดเจน การใช้งานโดยปริยายจำเป็นต้องเปิดเผยต่อสาธารณะ
เหตุผลสำหรับการดำเนินงานอย่างชัดเจนก็คือสำหรับการบำรุงรักษา
เมื่อชั้นเรียน "ไม่ว่าง" ใช่เกิดขึ้นเราทุกคนไม่มีความหรูหราในการปรับโครงสร้างโค้ดของสมาชิกในทีมคนอื่น ๆ - จากนั้นการมีการนำไปใช้อย่างชัดเจนทำให้ชัดเจนว่ามีวิธีการในการตอบสนองสัญญาอินเทอร์เฟซ
ดังนั้นจึงปรับปรุง "ความสามารถในการอ่าน" ของโค้ด
#region
คือสิ่งที่มีไว้สำหรับด้วยสตริงหัวเรื่องที่เหมาะสม และแสดงความคิดเห็นเกี่ยวกับวิธีการ
มีตัวอย่างที่แตกต่างออกไปSystem.Collections.Immutable
ซึ่งผู้เขียนเลือกที่จะใช้เทคนิคนี้เพื่อรักษา API ที่คุ้นเคยสำหรับประเภทคอลเลกชันในขณะที่ขูดส่วนต่างๆของอินเทอร์เฟซที่ไม่มีความหมายสำหรับประเภทใหม่
รูปธรรมImmutableList<T>
การดำเนินการIList<T>
และทำให้ICollection<T>
( เพื่อที่จะช่วยให้ImmutableList<T>
การใช้งานได้ง่ายมากขึ้นด้วยรหัสเดิม) ยังvoid ICollection<T>.Add(T item)
ทำให้รู้สึกไม่สำหรับImmutableList<T>
: ตั้งแต่การเพิ่มองค์ประกอบไปที่รายการที่ไม่เปลี่ยนรูปจะต้องไม่เปลี่ยนรายการที่มีอยู่ImmutableList<T>
นอกจากนี้ยังมาจากการIImmutableList<T>
ที่มีIImmutableList<T> Add(T item)
สามารถใช้สำหรับการ รายการที่ไม่เปลี่ยนรูป
ดังนั้นในกรณีของAdd
การนำไปใช้ในImmutableList<T>
ท้ายที่สุดมีดังนี้:
public ImmutableList<T> Add(T item)
{
// Create a new list with the added item
}
IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);
void ICollection<T>.Add(T item) => throw new NotSupportedException();
int IList.Add(object value) => throw new NotSupportedException();
ในกรณีของอินเทอร์เฟซที่กำหนดไว้อย่างชัดเจนวิธีการทั้งหมดจะเป็นแบบส่วนตัวโดยอัตโนมัติคุณไม่สามารถให้ตัวปรับแต่งการเข้าถึงแก่สาธารณะได้ สมมติ:
interface Iphone{
void Money();
}
interface Ipen{
void Price();
}
class Demo : Iphone, Ipen{
void Iphone.Money(){ //it is private you can't give public
Console.WriteLine("You have no money");
}
void Ipen.Price(){ //it is private you can't give public
Console.WriteLine("You have to paid 3$");
}
}
// So you have to cast to call the method
class Program
{
static void Main(string[] args)
{
Demo d = new Demo();
Iphone i1 = (Iphone)d;
i1.Money();
((Ipen)i1).Price();
Console.ReadKey();
}
}
// You can't call methods by direct class object
นี่คือวิธีที่เราสามารถสร้าง Explicit Interface: ถ้าเรามี 2 อินเทอร์เฟซและทั้งสองอินเทอร์เฟซมีเมธอดเดียวกันและคลาสเดียวจะสืบทอดอินเทอร์เฟซทั้ง 2 นี้ดังนั้นเมื่อเราเรียกวิธีอินเตอร์เฟสหนึ่งคอมไพเลอร์จะสับสนว่าจะเรียกเมธอดใดจึงจะสามารถ จัดการปัญหานี้โดยใช้ Explicit Interface นี่คือตัวอย่างหนึ่งที่ฉันให้ไว้ด้านล่าง
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oops3
{
interface I5
{
void getdata();
}
interface I6
{
void getdata();
}
class MyClass:I5,I6
{
void I5.getdata()
{
Console.WriteLine("I5 getdata called");
}
void I6.getdata()
{
Console.WriteLine("I6 getdata called");
}
static void Main(string[] args)
{
MyClass obj = new MyClass();
((I5)obj).getdata();
Console.ReadLine();
}
}
}