C# Language
इंटरफेस
खोज…
इंटरफ़ेस लागू करना
एक इंटरफ़ेस का उपयोग किसी भी वर्ग में एक विधि की उपस्थिति को लागू करने के लिए किया जाता है जो इसे लागू करता है। इंटरफ़ेस को कीवर्ड interface
साथ परिभाषित किया गया interface
और एक वर्ग इसे जोड़कर इसे 'कार्यान्वित' कर सकता है : InterfaceName
वर्ग नाम के बाद : InterfaceName
। प्रत्येक इंटरफ़ेस को अल्पविराम से अलग करके एक वर्ग कई इंटरफेस को लागू कर सकता है।
: InterfaceName, ISecondInterface
public interface INoiseMaker
{
string MakeNoise();
}
public class Cat : INoiseMaker
{
public string MakeNoise()
{
return "Nyan";
}
}
public class Dog : INoiseMaker
{
public string MakeNoise()
{
return "Woof";
}
}
क्योंकि वे INoiseMaker
लागू INoiseMaker
, cat
और dog
दोनों को string MakeNoise()
विधि को शामिल करने की आवश्यकता होती है और इसके बिना संकलन करने में विफल रहेंगे।
कई इंटरफेस को लागू करना
public interface IAnimal
{
string Name { get; set; }
}
public interface INoiseMaker
{
string MakeNoise();
}
public class Cat : IAnimal, INoiseMaker
{
public Cat()
{
Name = "Cat";
}
public string Name { get; set; }
public string MakeNoise()
{
return "Nyan";
}
}
स्पष्ट इंटरफ़ेस कार्यान्वयन
जब आप एक सामान्य विधि को परिभाषित करने वाले कई इंटरफेस को लागू करते हैं, तो स्पष्ट इंटरफ़ेस कार्यान्वयन आवश्यक है, लेकिन विधि को कॉल करने के लिए किस इंटरफ़ेस का उपयोग किया जा रहा है, इसके आधार पर अलग-अलग कार्यान्वयन आवश्यक हैं (ध्यान दें कि यदि एकाधिक इंटरफ़ेस समान विधि साझा करते हैं तो आपको स्पष्ट कार्यान्वयन की आवश्यकता नहीं है एक आम कार्यान्वयन संभव है)।
interface IChauffeur
{
string Drive();
}
interface IGolfPlayer
{
string Drive();
}
class GolfingChauffeur : IChauffeur, IGolfPlayer
{
public string Drive()
{
return "Vroom!";
}
string IGolfPlayer.Drive()
{
return "Took a swing...";
}
}
GolfingChauffeur obj = new GolfingChauffeur();
IChauffeur chauffeur = obj;
IGolfPlayer golfer = obj;
Console.WriteLine(obj.Drive()); // Vroom!
Console.WriteLine(chauffeur.Drive()); // Vroom!
Console.WriteLine(golfer.Drive()); // Took a swing...
इंटरफ़ेस का उपयोग करके कार्यान्वयन को कहीं और से नहीं बुलाया जा सकता है:
public class Golfer : IGolfPlayer
{
string IGolfPlayer.Drive()
{
return "Swinging hard...";
}
public void Swing()
{
Drive(); // Compiler error: No such method
}
}
इसके कारण, स्पष्ट रूप से कार्यान्वित किए गए इंटरफ़ेस के जटिल कार्यान्वयन कोड को एक अलग, निजी पद्धति में रखना फायदेमंद हो सकता है।
एक स्पष्ट इंटरफ़ेस कार्यान्वयन केवल उन तरीकों के लिए उपयोग किया जा सकता है जो वास्तव में उस इंटरफ़ेस के लिए मौजूद हैं:
public class ProGolfer : IGolfPlayer
{
string IGolfPlayer.Swear() // Error
{
return "The ball is in the pit";
}
}
इसी तरह, वर्ग पर उस इंटरफ़ेस को घोषित किए बिना एक स्पष्ट इंटरफ़ेस कार्यान्वयन का उपयोग करने से त्रुटि भी होती है।
संकेत:
स्पष्ट रूप से लागू इंटरफेस का उपयोग मृत कोड से बचने के लिए भी किया जा सकता है। जब किसी विधि की अब आवश्यकता नहीं है और इंटरफ़ेस से हटा दिया जाता है, तो कंपाइलर प्रत्येक मौजूदा मौजूदा कार्यान्वयन के बारे में शिकायत करेगा।
ध्यान दें:
प्रोग्रामर उम्मीद करते हैं कि अनुबंध समान प्रकार के संदर्भ के बिना ही होगा और स्पष्ट कार्यान्वयन को अलग-अलग व्यवहार को उजागर नहीं किया जाना चाहिए। इसलिए ऊपर दिए गए उदाहरण के विपरीत, IGolfPlayer.Drive
और Drive
को जब संभव हो तो यही काम करना चाहिए।
हम इंटरफेस का उपयोग क्यों करते हैं
एक इंटरफ़ेस इंटरफ़ेस के उपयोगकर्ता और इसे लागू करने वाले वर्ग के बीच एक अनुबंध की परिभाषा है। इंटरफ़ेस के बारे में सोचने का एक तरीका एक घोषणा के रूप में है कि एक वस्तु कुछ कार्य कर सकती है।
मान लें कि हम विभिन्न प्रकार की आकृतियों का प्रतिनिधित्व करने के लिए एक इंटरफ़ेस IShape
को परिभाषित करते हैं, हम एक क्षेत्र के लिए एक आकृति की अपेक्षा करते हैं, इसलिए हम इंटरफ़ेस कार्यान्वयन को उनके क्षेत्र को वापस करने के लिए बाध्य करने के लिए एक विधि को परिभाषित करेंगे:
public interface IShape
{
double ComputeArea();
}
मान लें कि हमारे पास दो आकृतियाँ हैं: एक Rectangle
और एक Circle
public class Rectangle : IShape
{
private double length;
private double width;
public Rectangle(double length, double width)
{
this.length = length;
this.width = width;
}
public double ComputeArea()
{
return length * width;
}
}
public class Circle : IShape
{
private double radius;
public Circle(double radius)
{
this.radius = radius;
}
public double ComputeArea()
{
return Math.Pow(radius, 2.0) * Math.PI;
}
}
उनमें से प्रत्येक की अपने क्षेत्र की अपनी परिभाषा है, लेकिन दोनों आकार हैं। इसलिए उन्हें हमारे कार्यक्रम में IShape
रूप में देखना केवल तर्कसंगत है:
private static void Main(string[] args)
{
var shapes = new List<IShape>() { new Rectangle(5, 10), new Circle(5) };
ComputeArea(shapes);
Console.ReadKey();
}
private static void ComputeArea(IEnumerable<IShape> shapes)
{
foreach (shape in shapes)
{
Console.WriteLine("Area: {0:N}, shape.ComputeArea());
}
}
// Output:
// Area : 50.00
// Area : 78.54
इंटरफ़ेस मूल बातें
इंटरफ़ेस का फ़ंक्शन कार्यक्षमता के "अनुबंध" के रूप में जाना जाता है। इसका मतलब यह है कि यह गुणों और विधियों की घोषणा करता है लेकिन यह उन्हें लागू नहीं करता है।
वर्गों के विपरीत तो इंटरफेस:
- तत्काल नहीं किया जा सकता
- कोई कार्यक्षमता नहीं हो सकती
- केवल विधियाँ शामिल हो सकती हैं * (गुण और घटनाएँ आंतरिक रूप से विधियाँ हैं)
- एक इंटरफ़ेस को इनहेरिट करना "कार्यान्वयन" कहा जाता है
- आप 1 वर्ग से विरासत में ले सकते हैं, लेकिन आप कई इंटरफेस को "लागू" कर सकते हैं
public interface ICanDoThis{
void TheThingICanDo();
int SomeValueProperty { get; set; }
}
ध्यान देने योग्य बातें:
- "I" उपसर्ग एक नामकरण सम्मेलन है जिसका उपयोग इंटरफेस के लिए किया जाता है।
- फ़ंक्शन बॉडी को अर्धविराम के साथ बदल दिया जाता है ";"।
- गुणों की भी अनुमति है क्योंकि आंतरिक रूप से वे भी विधियां हैं
public class MyClass : ICanDoThis {
public void TheThingICanDo(){
// do the thing
}
public int SomeValueProperty { get; set; }
public int SomeValueNotImplemtingAnything { get; set; }
}
।
ICanDoThis obj = new MyClass();
// ok
obj.TheThingICanDo();
// ok
obj.SomeValueProperty = 5;
// Error, this member doesn't exist in the interface
obj.SomeValueNotImplemtingAnything = 5;
// in order to access the property in the class you must "down cast" it
((MyClass)obj).SomeValueNotImplemtingAnything = 5; // ok
यह विशेष रूप से तब उपयोगी होता है जब आप UI फ्रेमवर्क जैसे कि WinForms या WPF के साथ काम कर रहे होते हैं क्योंकि उपयोगकर्ता नियंत्रण बनाने के लिए बेस क्लास से इनहेरिट करना अनिवार्य होता है और आप विभिन्न नियंत्रण प्रकारों पर अमूर्तता बनाने की क्षमता को ढीला कर देते हैं। एक उदाहरण? आ रहा है:
public class MyTextBlock : TextBlock {
public void SetText(string str){
this.Text = str;
}
}
public class MyButton : Button {
public void SetText(string str){
this.Content = str;
}
}
प्रस्तावित समस्या यह है कि दोनों में "पाठ" की कुछ अवधारणा है लेकिन संपत्ति के नाम अलग हैं। और आप एक सार आधार वर्ग नहीं बना सकते क्योंकि उनके पास 2 विभिन्न वर्गों के लिए एक अनिवार्य विरासत है। एक अंतरफलक है कि कम कर सकते हैं
public interface ITextControl{
void SetText(string str);
}
public class MyTextBlock : TextBlock, ITextControl {
public void SetText(string str){
this.Text = str;
}
}
public class MyButton : Button, ITextControl {
public void SetText(string str){
this.Content = str;
}
public int Clicks { get; set; }
}
अब MyButton और MyTextBlock विनिमेय है।
var controls = new List<ITextControls>{
new MyTextBlock(),
new MyButton()
};
foreach(var ctrl in controls){
ctrl.SetText("This text will be applied to both controls despite them being different");
// Compiler Error, no such member in interface
ctrl.Clicks = 0;
// Runtime Error because 1 class is in fact not a button which makes this cast invalid
((MyButton)ctrl).Clicks = 0;
/* the solution is to check the type first.
This is usually considered bad practice since
it's a symptom of poor abstraction */
var button = ctrl as MyButton;
if(button != null)
button.Clicks = 0; // no errors
}
स्पष्ट कार्यान्वयन के साथ "छिपाना" सदस्य
क्या आप इसे नफरत नहीं करते हैं जब इंटरफेस आपको कई सदस्यों के साथ वर्ग को प्रदूषित करता है जिसके बारे में आपको परवाह नहीं है? खैर मुझे एक उपाय सूझा! स्पष्ट कार्यान्वयन
public interface IMessageService {
void OnMessageRecieve();
void SendMessage();
string Result { get; set; }
int Encoding { get; set; }
// yadda yadda
}
आम तौर पर आप वर्ग को इस तरह लागू करेंगे।
public class MyObjectWithMessages : IMessageService {
public void OnMessageRecieve(){
}
public void SendMessage(){
}
public string Result { get; set; }
public int Encoding { get; set; }
}
हर सदस्य सार्वजनिक है।
var obj = new MyObjectWithMessages();
// why would i want to call this function?
obj.OnMessageRecieve();
उत्तर: मैं नहीं। इसलिए न तो इसे सार्वजनिक घोषित किया जाना चाहिए, बल्कि सदस्यों को निजी घोषित करने से कंपाइलर को कोई त्रुटि होगी
समाधान स्पष्ट कार्यान्वयन का उपयोग करना है:
public class MyObjectWithMessages : IMessageService{
void IMessageService.OnMessageRecieve() {
}
void IMessageService.SendMessage() {
}
string IMessageService.Result { get; set; }
int IMessageService.Encoding { get; set; }
}
इसलिए अब आपने सदस्यों को आवश्यकतानुसार लागू कर दिया है और वे किसी भी सदस्य को सार्वजनिक रूप से उजागर नहीं करेंगे।
var obj = new MyObjectWithMessages();
/* error member does not exist on type MyObjectWithMessages.
* We've succesfully made it "private" */
obj.OnMessageRecieve();
यदि आप गंभीरता से अभी भी सदस्य का उपयोग करना चाहते हैं, भले ही आपको स्पष्ट रूप से लागू करना है, तो आपको इंटरफ़ेस पर ऑब्जेक्ट डालना है और आपको जाना अच्छा है।
((IMessageService)obj).OnMessageRecieve();
IComparable एक इंटरफ़ेस को लागू करने के एक उदाहरण के रूप में
जब तक आप उन्हें व्यवहार में नहीं लेते हैं, तब तक इंटरफेस अमूर्त लग सकता है। IComparable
और IComparable<T>
महान उदाहरण हैं कि क्यों इंटरफेस हमारे लिए मददगार हो सकते हैं।
मान लीजिए कि ऑनलाइन स्टोर के लिए एक कार्यक्रम में, हमारे पास विभिन्न प्रकार के आइटम हैं जिन्हें आप खरीद सकते हैं। प्रत्येक आइटम में एक नाम, एक आईडी नंबर और एक मूल्य होता है।
public class Item {
public string name; // though public variables are generally bad practice,
public int idNumber; // to keep this example simple we will use them instead
public decimal price; // of a property.
// body omitted for brevity
}
हमारे पास हमारे Item
को एक List<Item>
अंदर संग्रहीत किया जाता है, और हमारे कार्यक्रम में कहीं न कहीं, हम अपनी सूची को आईडी संख्या से सबसे छोटे से सबसे बड़े तक क्रमबद्ध करना चाहते हैं। अपने स्वयं के सॉर्टिंग एल्गोरिदम को लिखने के बजाय, हम Sort()
पद्धति का उपयोग कर सकते हैं जो List<T>
पहले से है। हालाँकि, जैसा कि हमारा Item
वर्ग अभी है, List<T>
को क्रमबद्ध करने के लिए List<T>
का कोई तरीका नहीं है। यहाँ वह जगह है जहाँ IComparable
इंटरफ़ेस आता है।
सही ढंग से लागू करने के लिए CompareTo
विधि, CompareTo
एक सकारात्मक संख्या लौटना चाहिए यदि पैरामीटर वर्तमान एक, शून्य वे बराबर हैं, और एक नकारात्मक संख्या है, तो अगर पैरामीटर "से अधिक" है "से कम है।"
Item apple = new Item();
apple.idNumber = 15;
Item banana = new Item();
banana.idNumber = 4;
Item cow = new Item();
cow.idNumber = 15;
Item diamond = new Item();
diamond.idNumber = 18;
Console.WriteLine(apple.CompareTo(banana)); // 11
Console.WriteLine(apple.CompareTo(cow)); // 0
Console.WriteLine(apple.CompareTo(diamond)); // -3
यहाँ उदाहरण है इंटरफ़ेस का Item
कार्यान्वयन:
public class Item : IComparable<Item> {
private string name;
private int idNumber;
private decimal price;
public int CompareTo(Item otherItem) {
return (this.idNumber - otherItem.idNumber);
}
// rest of code omitted for brevity
}
एक सतह के स्तर पर, CompareTo
हमारे मद में विधि केवल अपने आईडी नंबर में अंतर देता है, लेकिन क्या इसके बाद के संस्करण व्यवहार में क्या करता है?
अब, जब हम कहते हैं Sort()
एक पर List<Item>
वस्तु, List
स्वचालित रूप से कॉल करेंगे Item
के CompareTo
विधि, जब यह निर्धारित करने के लिए किस क्रम में वस्तुओं डाल करने के लिए की जरूरत है। इसके अलावा के अलावा List<T>
, किसी भी अन्य वस्तुओं दो वस्तुओं की तुलना करने की क्षमता की आवश्यकता Item
साथ काम करेगी क्योंकि हमने दो अलग-अलग Item
की एक दूसरे के साथ तुलना करने की क्षमता को परिभाषित किया है।