मैं C # में अमूर्त स्थिर तरीके क्यों नहीं कर सकता?


181

मैं हाल ही में प्रदाताओं के साथ काम कर रहा हूं, और मैं एक दिलचस्प स्थिति में आया हूं जहां मैं एक सार वर्ग रखना चाहता था जिसमें एक सार स्थिर विधि थी। मैं विषय पर कुछ पोस्ट पढ़ता हूं, और यह एक तरह से बना हुआ अर्थ है, लेकिन क्या एक अच्छी व्याख्या है?


3
भविष्य के सुधार की अनुमति देने के लिए कृपया इन्हें खुला छोड़ दें।
मार्क बेइक

6
मुझे लगता है कि सवाल इस तथ्य पर हो रहा है कि इस तरह की स्थिति के लिए C # को एक और कीवर्ड की आवश्यकता है। आप एक ऐसी विधि चाहते हैं जिसका वापसी मूल्य केवल उस प्रकार पर निर्भर करता है जिस पर इसे कहा जाता है। यदि अज्ञात है, तो आप इसे "स्थिर" नहीं कह सकते। लेकिन एक बार प्रकार ज्ञात हो जाने के बाद, यह स्थिर हो जाएगा। "अनारक्षित स्थैतिक" यह विचार है - यह अभी तक स्थिर नहीं है, लेकिन एक बार जब हम प्राप्त करने वाले प्रकार को जानते हैं, तो यह होगा। यह पूरी तरह से एक अच्छी अवधारणा है, यही वजह है कि प्रोग्रामर इसके लिए पूछते रहते हैं। लेकिन यह उस तरह से उपयुक्त नहीं था जिस तरह से डिजाइनर भाषा के बारे में सोचते थे।
विलियम जॉकस

@WilliamJockusch को प्राप्त करने का क्या मतलब है? अगर मैं BaseClass.StaticMethod () कहता हूं, तो BaseClass एकमात्र प्रकार है जिसका उपयोग वह निर्णय लेने के लिए कर सकता है। लेकिन इस स्तर पर यह अमूर्त है इसलिए विधि को हल नहीं किया जा सकता है। यदि आप इसके बजाय DerivedClass.StaticMethod को अच्छी तरह से कॉल करते हैं तो बेस क्लास अप्रासंगिक है।
मार्टिन कैपोडिसी

बेस क्लास में, विधि अनसुलझी है, और आप इसका उपयोग नहीं कर सकते हैं। आपको या तो एक व्युत्पन्न प्रकार या एक वस्तु की आवश्यकता है (जो बदले में एक व्युत्पन्न प्रकार होगा)। आपको baseClassObject.Method () या DerivedClass.Method () को कॉल करने में सक्षम होना चाहिए। आप BaseClass.Method () को कॉल नहीं कर सकते क्योंकि यह आपको टाइप नहीं देता है।
विलियम जॉकस

जवाबों:


156

स्थैतिक तरीकों को तत्काल नहीं दिया जाता है, वे सिर्फ एक वस्तु संदर्भ के बिना उपलब्ध हैं।

एक स्थिर विधि के लिए एक कॉल क्लास के नाम के माध्यम से किया जाता है, एक वस्तु संदर्भ के माध्यम से नहीं, और इंटरमीडिएट भाषा (आईएल) कोड को कॉल करने के लिए सार विधि को उस वर्ग के नाम के माध्यम से कॉल किया जाएगा जिसने इसे परिभाषित किया है, जरूरी नहीं कि नाम वह वर्ग जिसका आपने उपयोग किया था।

मुझे एक उदाहरण दिखाते हैं।

निम्नलिखित कोड के साथ:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

यदि आप B.Test, को इस तरह कहते हैं:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

फिर मुख्य विधि के अंदर वास्तविक कोड इस प्रकार है:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

जैसा कि आप देख सकते हैं, कॉल A.Test को किया जाता है, क्योंकि यह ए क्लास था जिसने इसे परिभाषित किया था, और बी.टेस्ट को नहीं, भले ही आप उस तरह से कोड लिख सकते हैं।

यदि आपके पास वर्ग प्रकार हैं , जैसे डेल्फी में, जहां आप एक प्रकार का संदर्भ देते हुए एक चर बना सकते हैं और एक वस्तु नहीं, तो आपके पास आभासी के लिए अधिक उपयोग होगा और इस प्रकार अमूर्त स्थिर तरीके (और कंस्ट्रक्टर भी) होंगे, लेकिन वे उपलब्ध नहीं हैं और इस प्रकार स्थिर कॉल .NET में गैर-आभासी हैं।

मुझे एहसास है कि IL डिज़ाइनर B.Test को कॉल करने के लिए कोड को संकलित करने की अनुमति दे सकते हैं, और रनटाइम पर कॉल को हल कर सकते हैं, लेकिन यह अभी भी आभासी नहीं होगा, क्योंकि आपको अभी भी वहां किसी प्रकार का वर्ग नाम लिखना होगा।

वर्चुअल मेथड्स, और इस प्रकार एब्सट्रैक्ट, केवल तभी उपयोगी होते हैं जब आप एक वैरिएबल का उपयोग कर रहे होते हैं, जो रनटाइम के दौरान, कई अलग-अलग प्रकार की ऑब्जेक्ट्स को शामिल कर सकते हैं, और आप इस प्रकार वेरिएबल में मौजूद करंट ऑब्जेक्ट के लिए सही मेथड को कॉल करना चाहते हैं। स्थिर तरीकों से आपको किसी भी वर्ग के नाम से गुजरने की आवश्यकता होती है, इसलिए कॉल करने का सटीक तरीका संकलन समय पर जाना जाता है क्योंकि यह बदल नहीं सकता है।

इस प्रकार, .NET में वर्चुअल / एब्स्ट्रैक्ट स्टैटिक मेथड उपलब्ध नहीं हैं।


4
जिस तरह से ऑपरेटर-ओवरलोडिंग सी # में की जाती है, उससे संयुक्त रूप से, यह दुर्भाग्य से किसी दिए गए ऑपरेटर अधिभार के लिए एक कार्यान्वयन प्रदान करने के लिए उप-वर्ग की आवश्यकता की संभावना को समाप्त करता है।
क्रिस मोसचिनी

23
मैं नहीं इस जवाब परिभाषा के रूप में बहुत उपयोगी का मिल रहा है Test()में है Aसार और संभवतः में परिभाषित किया जा रहा है के बजाय B\।

5
सामान्य प्रकार के पैरामीटर प्रभावी रूप से गैर-स्थायी "प्रकार" चर के रूप में व्यवहार करते हैं, और आभासी स्थिर तरीके ऐसे संदर्भ में उपयोगी हो सकते हैं। उदाहरण के लिए, यदि किसी के पास Carवर्चुअल स्टैटिक CreateFromDescriptionफ़ैक्टरी विधि के साथ एक प्रकार होता है , तो कोड जो एक- Carप्रकार के जेनरिक प्रकार Tको स्वीकार करता है T.CreateFromDescription, प्रकार की कार बनाने के लिए कॉल कर सकता है T। इस तरह के एक निर्माण को सीएलआर के भीतर बहुत अच्छी तरह से समर्थित किया जा सकता है यदि प्रत्येक प्रकार जो इस तरह की विधि को परिभाषित करता है एक नेस्टेड क्लास जेनेरिक का एक स्थिर सिंगलटन उदाहरण होता है जो आभासी "स्थिर" विधियों का आयोजन करता है।
सुपरकाट

45

स्थैतिक विधियों को विरासत में या अधिग्रहित नहीं किया जा सकता है, और यही कारण है कि वे सार नहीं हो सकते हैं। चूँकि स्थैतिक विधियाँ प्रकार पर परिभाषित होती हैं, उदाहरण के लिए नहीं, एक वर्ग के रूप में, उन्हें उस प्रकार पर स्पष्ट रूप से कहा जाना चाहिए। इसलिए जब आप चाइल्ड क्लास पर एक विधि को कॉल करना चाहते हैं, तो आपको इसे कॉल करने के लिए इसके नाम का उपयोग करने की आवश्यकता है। इससे वंशानुक्रम अप्रासंगिक हो जाता है।

मान लें कि आप एक पल के लिए स्थैतिक तरीकों को अपना सकते हैं। इस परिदृश्य की कल्पना करें:

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

यदि आप Base.GetNumber () कहते हैं, तो किस विधि को कहा जाएगा? कौन सा मान लौटा? यह देखने के लिए बहुत आसान है कि वस्तुओं के उदाहरणों को बनाए बिना, वंशानुक्रम कठिन है। वंशानुक्रम के बिना सार विधियां सिर्फ ऐसी विधियां हैं जिनमें शरीर नहीं है, इसलिए इसे नहीं बुलाया जा सकता है।


33
आपके परिदृश्य को देखते हुए मैं कहूंगा कि बेस.गनेटनंबर () 5 वापस आ जाएगा; चाइल्ड 1.गेटनंबर () 1 रिटर्न; चाइल्ड 2.गेटनंबर () 2 रिटर्न; क्या आप मुझे गलत साबित कर सकते हैं, मुझे आपके तर्क को समझने में मदद करने के लिए? धन्यवाद
लुइस फिलीप ऑक्ट

वह चेहरा जो आप सोचते हैं कि बेस.गनेटनंबर (5) रिटर्न का मतलब है कि आप पहले से ही समझते हैं कि क्या चल रहा है। आधार मूल्य वापस करके, कोई विरासत नहीं चल रही है।
डेविड वेंगर 22

60
क्यों दुनिया में Base.GetNumber () 5 और कुछ भी लौटाएगा? यह बेस क्लास में एक विधि है - वहां केवल 1 विकल्प है।
आर्टेम रसाकोवस्की

4
@ArtemRussakovskii: मान लीजिए कि एक था int DoSomething<T>() where T:Base {return T.GetNumber();}। यह उपयोगी लगता है अगर DoSomething<Base>()पांच लौट सकते हैं, जबकि DoSomething<Child2>()दो वापस आ जाएंगे। इस तरह की क्षमता न केवल खिलौने के उदाहरणों के लिए उपयोगी होगी, बल्कि कुछ ऐसी चीज़ों के लिए भी होगी class Car {public static virtual Car Build(PurchaseOrder PO);}, जहाँ से प्राप्त होने वाली प्रत्येक कक्षा Carको एक पद्धति को परिभाषित करना होगा, जो खरीद ऑर्डर दिए गए उदाहरण का निर्माण कर सकती है।
Supercat

4
गैर-स्थैतिक विरासत के साथ बिल्कुल समान "समस्या" है।
अर्क-कुन

18

एक अन्य प्रतिवादी (मैकडॉवेल) ने कहा कि बहुरूपता केवल वस्तु उदाहरण के लिए काम करता है। वह योग्य होना चाहिए; ऐसी भाषाएं हैं जो कक्षाओं को "क्लास" या "मेटाक्लास" प्रकार के उदाहरणों के रूप में मानते हैं। ये भाषाएं उदाहरण और वर्ग (स्थिर) तरीकों के लिए बहुरूपता का समर्थन करती हैं।

C #, जावा और C ++ से पहले, ऐसी भाषा नहीं है; staticकीवर्ड निरूपित करने के लिए है कि विधि स्थिर-बाध्य है की बजाय गतिशील / आभासी स्पष्ट रूप से प्रयोग किया जाता है।


9

यहाँ एक ऐसी स्थिति है जहाँ निश्चित रूप से स्थैतिक क्षेत्रों और विधियों के लिए विरासत की आवश्यकता है:

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?

4
नहीं, सरणी 'पैर' का केवल एक उदाहरण है। आउटपुट nondeterministic है जैसा कि आप नहीं जानते कि स्थिर कंस्ट्रक्टरों को किस क्रम में बुलाया जाएगा (वास्तव में कोई गारंटी नहीं है कि बेस क्लास स्टैटिक कंस्ट्रक्टर को बिल्कुल नहीं कहा जाएगा)। 'आवश्यकता' एक पूर्ण निरपेक्ष शब्द है जहाँ 'इच्छा' शायद अधिक सटीक है।
सैम

legsएक स्थिर अमूर्त संपत्ति होनी चाहिए।
छोटे एंडियन

8

पिछली व्याख्याओं में जोड़ने के लिए, स्थिर विधि कॉल संकलन-समय पर एक विशिष्ट विधि से बाध्य होती है , जो बहुरूपता व्यवहार को नियंत्रित करती है।


C # स्टेटिकली टाइप्ड है; पॉलीमॉर्फिक विधियों के लिए कॉल भी संकलित समय पर बाध्य होते हैं क्योंकि मैं इसे समझता हूं - यह कहना है कि सीएलआर को रनटाइम के दौरान कॉल करने के लिए किस विधि को हल करने के लिए नहीं छोड़ा गया है।
एडम टोली

तो आपको कैसे लगता है कि बहुरूपता सीएलआर पर काम करता है? आपकी व्याख्या ने केवल आभासी विधि प्रेषण को खारिज कर दिया।
Rytmis

यह वास्तव में एक टिप्पणी के रूप में उपयोगी नहीं है क्योंकि यह हो सकता है। मैंने आमंत्रित किया (invited जैसा कि मैं इसे समझता हूं ’) उपयोगी प्रवचन, लगता है कि शायद आप कुछ और सामग्री प्रदान कर सकते हैं - यह देखकर कि लोग यहां आकर जवाब तलाश रहे हैं और अपमान नहीं। हालांकि, ऐसा लगता है कि मैं एक ही बात का दोषी हो सकता हूं - मैं वास्तव में एक टिप्पणी के रूप में उपरोक्त टिप्पणी का मतलब है: संकलन समय पर इन बातों का मूल्यांकन नहीं करता है?
एडम टोली

माफी, मेरा मतलब अपमान नहीं था (हालांकि मैं थोड़ा तड़क जवाब देने के लिए मानता हूं; ;-) मेरे प्रश्न का मुद्दा यह था, अगर आपको ये कक्षाएं मिली हैं: वर्ग आधार {सार्वजनिक आभासी शून्य विधि (); } वर्ग व्युत्पन्न: बेस {सार्वजनिक ओवरराइड शून्य विधि (); } और इस प्रकार लिखें: आधार उदाहरण = नया व्युत्पन्न (); instance.Method (); कॉल साइट पर संकलन-समय प्रकार की जानकारी यह है कि हमें बेस का एक उदाहरण मिला है, जब वास्तविक उदाहरण एक व्युत्पन्न है। इसलिए कंपाइलर कॉल करने की सटीक विधि को हल नहीं कर सकता है। इसके बजाय यह एक "कॉलवर्ट" आईएल निर्देश का उत्सर्जन करता है, जो रनटाइम को भेजने के लिए कहता है ..
रिट्मिस

1
धन्यवाद आदमी, जानकारीपूर्ण! लगता है कि मैं लंबे समय से आईएल में गोता लगा रहा हूं, मुझे शुभकामनाएं।
एडम टोली

5

हम वास्तव में स्थिर तरीकों (डेल्फी में) से आगे निकल जाते हैं, यह थोड़ा बदसूरत है, लेकिन यह हमारी जरूरतों के लिए ठीक काम करता है।

हम इसका उपयोग करते हैं इसलिए कक्षाओं में बिना वर्ग उदाहरण के उनकी उपलब्ध वस्तुओं की एक सूची हो सकती है, उदाहरण के लिए, हमारे पास एक तरीका है जो इस तरह दिखता है:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

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

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

इसलिए प्रत्येक क्लाइंट के लिए एक अलग सर्वर एप्लिकेशन होने से इसे बनाए रखना बहुत आसान है।

आशा है कि उदाहरण स्पष्ट था।


0

अमूर्त तरीके अंतर्निहित आभासी हैं। अमूर्त तरीकों के लिए एक उदाहरण की आवश्यकता होती है, लेकिन स्थिर विधियों में एक उदाहरण नहीं होता है। तो, आपके पास एक अमूर्त वर्ग में एक स्थिर विधि हो सकती है, यह सिर्फ स्थिर सार (या सार स्थिर) नहीं हो सकती है।


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