C # इंटरफ़ेस विधियों को सार या आभासी घोषित क्यों नहीं किया जाता है?


108

इंटरफेस के बिना सी # तरीके virtualकीवर्ड का उपयोग किए बिना घोषित किए जाते हैं , और overrideकीवर्ड का उपयोग किए बिना व्युत्पन्न वर्ग में ओवरराइड किया जाता है ।

क्या इसका कोई कारण है? मुझे लगता है कि यह सिर्फ एक भाषा की सुविधा है, और जाहिर है सीएलआर जानता है कि इसे कैसे कवर के तहत संभालना है (तरीके डिफ़ॉल्ट रूप से आभासी नहीं हैं), लेकिन क्या अन्य तकनीकी कारण हैं?

यहाँ IL है कि एक व्युत्पन्न वर्ग उत्पन्न करता है:

class Example : IDisposable {
    public void Dispose() { }
}

.method public hidebysig newslot virtual final 
        instance void  Dispose() cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method Example::Dispose

ध्यान दें कि विधि virtual finalIL में घोषित की गई है।

जवाबों:


145

इंटरफ़ेस के लिए abstract, या इसके अलावा publicकीवर्ड बेमानी होंगे, इसलिए आप उन्हें छोड़ देते हैं:

interface MyInterface {
  void Method();
}

CIL में, विधि चिह्नित है virtualऔर abstract

(ध्यान दें कि जावा इंटरफ़ेस सदस्यों को घोषित करने की अनुमति देता है public abstract)।

कार्यान्वयन वर्ग के लिए, कुछ विकल्प हैं:

नॉन-ओवररिडेबल : C # में क्लास विधि को घोषित नहीं करता है virtual। इसका मतलब है कि इसे एक व्युत्पन्न वर्ग (केवल छिपे हुए) में ओवरराइड नहीं किया जा सकता है। CIL में विधि अभी भी आभासी (लेकिन सील) है क्योंकि इसे इंटरफ़ेस प्रकार के बारे में बहुरूपता का समर्थन करना चाहिए।

class MyClass : MyInterface {
  public void Method() {}
}

ओवररिडेबल : C # और CIL दोनों में विधि है virtual। यह बहुरूपी प्रेषण में भाग लेता है और इसे ओवरराइड किया जा सकता है।

class MyClass : MyInterface {
  public virtual void Method() {}
}

स्पष्ट : यह एक वर्ग के लिए एक अंतरफलक को लागू करने का एक तरीका है, लेकिन कक्षा के सार्वजनिक इंटरफ़ेस में इंटरफ़ेस के तरीके प्रदान नहीं करता है। CIL में विधि private(!) होगी, लेकिन यह तब भी क्लास के बाहर से संबंधित इंटरफ़ेस प्रकार के संदर्भ में कॉल करने योग्य होगी। स्पष्ट कार्यान्वयन भी गैर-अतिदेय हैं। यह संभव है क्योंकि एक CIL निर्देश ( .override) है जो निजी पद्धति को संबंधित इंटरफ़ेस विधि से जोड़ देगा जो इसे लागू कर रहा है।

[सी#]

class MyClass : MyInterface {
  void MyInterface.Method() {}
}

[सीआईएल]

.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed
{
  .override MyInterface::Method
}

VB.NET में, आप कार्यान्वयन वर्ग में इंटरफ़ेस विधि का नाम भी लिख सकते हैं।

[VB.NET]

Public Class MyClass
  Implements MyInterface
  Public Sub AliasedMethod() Implements MyInterface.Method
  End Sub
End Class

[सीआईएल]

.method public newslot virtual final instance void AliasedMethod() cil managed
{
  .override MyInterface::Method
}

अब, इस अजीब मामले पर विचार करें:

interface MyInterface {
  void Method();
}
class Base {
  public void Method();
}
class Derived : Base, MyInterface { }

यदि Baseऔर Derivedएक ही विधानसभा में घोषित किए जाते हैं, तो संकलक Base::Methodवर्चुअल और सील (CIL में) बना देगा, भले ही Baseइंटरफ़ेस को लागू नहीं करता है।

तो Baseऔर Derivedविभिन्न विधानसभाओं में कर रहे हैं, जब संकलन Derivedविधानसभा, संकलक अन्य विधानसभा में परिवर्तन नहीं होगा, तो यह में एक सदस्य का परिचय देंगे Derivedकि के लिए एक स्पष्ट कार्यान्वयन किया जाएगा MyInterface::Methodकि बस करने के लिए कॉल प्रतिनिधि होगा Base::Method

तो आप देखते हैं, प्रत्येक इंटरफ़ेस विधि कार्यान्वयन को पॉलीमॉर्फिक व्यवहार का समर्थन करना चाहिए, और इस प्रकार CIL पर वर्चुअल चिह्नित किया जाना चाहिए, भले ही संकलक को इसे करने के लिए हुप्स से गुजरना पड़े।


73

यहाँ CSharp 3 संस्करण के माध्यम से CLR से जेफरी रिचर का हवाला देते हुए

CLR के लिए आवश्यक है कि इंटरफ़ेस विधियों को आभासी के रूप में चिह्नित किया जाए। यदि आप स्पष्ट रूप से विधि को अपने स्रोत कोड में आभासी के रूप में चिह्नित नहीं करते हैं, तो संकलक विधि को आभासी और सील के रूप में चिह्नित करता है; यह एक व्युत्पन्न वर्ग को इंटरफ़ेस विधि को ओवरराइड करने से रोकता है। यदि आप स्पष्ट रूप से विधि को आभासी के रूप में चिह्नित करते हैं, तो संकलक विधि को आभासी के रूप में चिह्नित करता है (और इसे अनसुना छोड़ देता है); यह एक व्युत्पन्न वर्ग को इंटरफ़ेस विधि को ओवरराइड करने की अनुमति देता है। यदि कोई इंटरफ़ेस विधि सील है, तो एक व्युत्पन्न वर्ग विधि को ओवरराइड नहीं कर सकता है। हालांकि, एक व्युत्पन्न वर्ग उसी इंटरफ़ेस को फिर से इनहेरिट कर सकता है और इंटरफ़ेस के तरीकों के लिए अपना स्वयं का कार्यान्वयन प्रदान कर सकता है।


25
बोली यह नहीं कहती है कि वर्चुअल को चिह्नित करने के लिए एक इंटरफ़ेस विधि का कार्यान्वयन क्यों आवश्यक है। यह इसलिए है क्योंकि यह इंटरफ़ेस प्रकार के संबंध में बहुरूपी है, इसलिए इसे वर्चुअल टेबल पर वर्चुअल विधि भेजने की अनुमति देने के लिए एक स्लॉट की आवश्यकता होती है ।
जोर्डो सेप

1
मुझे स्पष्ट रूप से वर्चुअल के रूप में इंटरफ़ेस विधि को चिह्नित करने की अनुमति नहीं है, और "त्रुटि CS0106: त्रुटि प्राप्त करें" संशोधक 'वर्चुअल' इस आइटम के लिए मान्य नहीं है। V2.0.50727 (मेरे पीसी पर सबसे पुराना संस्करण) का उपयोग करके परीक्षण किया गया।
ccppjava

3
नीचे दिए गए जोराडो की टिप्पणी से @ccppjava, आप वर्ग सदस्य को चिह्नित करते हैं जो वर्ग को ओवरराइड करने के लिए उपवर्गों के लिए अनुमति देने के लिए इंटरफ़ेस वर्चुअल को लागू कर रहा है।
क्रिस्टोफर स्टीवेंसन

13

हां, जहां तक ​​रनटाइम का संबंध है, इंटरफ़ेस कार्यान्वयन के तरीके आभासी हैं। यह एक कार्यान्वयन विवरण है, यह इंटरफेस काम करता है। वर्चुअल तरीकों से क्लास की वी-टेबल में स्लॉट मिलते हैं, प्रत्येक स्लॉट में वर्चुअल तरीकों में से एक के लिए एक पॉइंटर होता है। एक इंटरफ़ेस प्रकार के लिए ऑब्जेक्ट को कास्टिंग करना तालिका के अनुभाग में एक संकेतक उत्पन्न करता है जो इंटरफ़ेस विधियों को लागू करता है। इंटरफ़ेस कोड का उपयोग करने वाला क्लाइंट कोड अब इंटरफ़ेस सूचक, वगैरह से ऑफसेट 0 पर पहला इंटरफ़ेस विधि सूचक देखता है।

मैंने अपने मूल उत्तर में जो बात रेखांकित की है वह अंतिम विशेषता का महत्व है । यह एक व्युत्पन्न वर्ग को आभासी पद्धति को ओवरराइड करने से रोकता है। एक व्युत्पन्न वर्ग को इंटरफ़ेस को फिर से लागू करना चाहिए, कार्यान्वयन के तरीके आधार वर्ग के तरीकों को छाया देते हैं। जो सी # भाषा अनुबंध को लागू करने के लिए पर्याप्त है जो कहता है कि कार्यान्वयन विधि आभासी नहीं है।

यदि आप उदाहरण वर्ग में डिस्पोज़ () विधि को आभासी घोषित करते हैं, तो आप देखेंगे कि अंतिम विशेषता हटा दी जाएगी। अब एक व्युत्पन्न वर्ग को इसे ओवरराइड करने की अनुमति देता है।


4

अधिकांश अन्य संकलित कोड वातावरणों में, इंटरफ़ेस को vtables के रूप में लागू किया जाता है - विधि निकायों के संकेत की एक सूची। आमतौर पर एक वर्ग जो कई इंटरफेस को लागू करता है, उसके आंतरिक संकलक में कहीं-कहीं मेटाडेटा से इंटरफ़ेस vtables की सूची, प्रति इंटरफ़ेस एक vtable (ताकि विधि क्रम संरक्षित हो) होगा। यह कैसे COM इंटरफेस आमतौर पर के रूप में अच्छी तरह से लागू कर रहे हैं।

.NET में, हालांकि, इंटरफ़ेस को प्रत्येक वर्ग के लिए अलग vtables के रूप में लागू नहीं किया गया है। इंटरफ़ेस विधियों को एक वैश्विक इंटरफ़ेस विधि तालिका के माध्यम से अनुक्रमित किया जाता है जो सभी इंटरफेस का एक हिस्सा है। इसलिए, इंटरफ़ेस विधि को लागू करने के लिए उस विधि के लिए एक विधि को आभासी घोषित करना आवश्यक नहीं है - वैश्विक इंटरफ़ेस विधि तालिका सीधे वर्ग की विधि के कोड पते को इंगित कर सकती है।

इंटरफ़ेस को लागू करने के लिए एक विधि आभासी घोषित करना अन्य भाषाओं में भी गैर-सीएलआर प्लेटफार्मों में आवश्यक नहीं है। Win32 पर डेल्फी भाषा एक उदाहरण है।


0

वे आभासी नहीं हैं (हम उनके बारे में कैसे सोचते हैं, यदि अंतर्निहित कार्यान्वयन के संदर्भ में नहीं है (सीलबंद आभासी) - तो यहां अन्य उत्तरों को पढ़ने और खुद कुछ सीखने के लिए अच्छा है :-)

वे कुछ भी ओवरराइड नहीं करते हैं - इंटरफ़ेस में कोई कार्यान्वयन नहीं है।

सभी इंटरफ़ेस एक "अनुबंध" की आपूर्ति करता है जिसे वर्ग को पालन करना पड़ता है - एक पैटर्न, यदि आपको पसंद है, ताकि कॉल करने वालों को पता हो कि वस्तु को कैसे कॉल करना है, भले ही उन्होंने उस विशेष वर्ग को पहले कभी नहीं देखा हो।

यह तब तक क्लास पर निर्भर है, जब तक कि यह अनुबंध के दायरे के भीतर - जैसे वर्चुअल या "गैर-आभासी" के रूप में इंटरफ़ेस विधि को लागू करेगा (सील आभासी जैसा कि यह निकला)।


इस सूत्र में हर कोई जानता है कि इंटरफेस किस लिए हैं। प्रश्न अत्यंत विशिष्ट है - उत्पन्न आईएल है इंटरफ़ेस विधि के लिए आभासी और एक गैर इंटरफ़ेस विधि के लिए आभासी नहीं।
रेक्स एम

5
हाँ, प्रश्न को संपादित करने के बाद किसी उत्तर की आलोचना करना वास्तव में आसान है, है ना?
जेसन विलियम्स

0

जब आप एक वर्ग है कि लागू एक अंतरफलक, तुम बस कह "वर्ग इंटरफ़ेस से इन विशेष तरीकों होना आवश्यक घोषित इंटरफेस, वर्गों की तुलना में एक अधिक अमूर्त अवधारणा है, और करता है बात नहीं wheter स्थिर , आभासी , गैर आभासी , ओवरराइड , के रूप में जब तक उसके पास समान आईडी और समान प्रकार के पैरामीटर हैं ”।

अन्य भाषाएं जो इस तरह के ऑब्जेक्ट पास्कल ("डेल्फी") और ऑब्जेक्टिव-सी (मैक) का समर्थन करती हैं, को इंटरफ़ेस के तरीकों को वर्चुअल चिह्नित करने की आवश्यकता नहीं है और न ही आभासी।

लेकिन, आप सही हो सकते हैं, मुझे लगता है कि इंटरफेस में विशिष्ट "वर्चुअल" / "ओवरराइड" विशेषता रखने के लिए एक अच्छा विचार हो सकता है, यदि आप उन वर्गों के तरीकों को प्रतिबंधित करना चाहते हैं जो किसी विशेष इंटरफ़ेस को लागू करते हैं। लेकिन, इसका मतलब यह भी है कि दोनों इंटरफेस के लिए एक "नॉन-आर्टिकल", "नॉटकेयरिफायरवर्थलॉट" कीवर्ड हैं।

मैं आपके प्रश्न को समझता हूं, क्योंकि मुझे जावा में कुछ समान दिखाई देता है, जब एक वर्ग विधि को "@virtual" या "@override" का उपयोग करना होता है ताकि यह सुनिश्चित हो सके कि एक विधि आभासी होने का इरादा है।


@override वास्तव में कोड के व्यवहार को नहीं बदलता है या परिणामी बाइट कोड को बदल देता है। यह जो करता है वह संकलक को संकेत देता है कि जिस विधि से सजाया गया है उसका उद्देश्य एक ओवरराइड है, जो संकलक को कुछ पवित्रता जांच करने की अनुमति देता है। सी # अलग तरीके से काम करता है; overrideभाषा में एक प्रथम श्रेणी का कीवर्ड है।
रॉबर्ट हार्वे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.