अद्यतन: यह सवाल जनवरी 2010 में मेरे ब्लॉग का विषय था। महान प्रश्न के लिए धन्यवाद! देखें:
https://blogs.msdn.microsoft.com/ericlippert/2010/01/14/why-cant-i-access-a-protected-member-from-a-derived-class-part-six/
है किसी को भी एक समस्या यह है कि वजह से हो जाएगा द्वारा दे प्रकार YourDerived या बेस के एक चर के माध्यम से पहुँच बेस के संरक्षित सदस्यों MyDerived का एक उदाहरण है, लेकिन करता है पहले से मौजूद नहीं है जब उन्हें का उपयोग MyDerived या MySuperDerived प्रकार के चर के माध्यम से किया जा रहा है?
मैं आपके प्रश्न से उलझन में हूं लेकिन मैं इसे एक शॉट देने के लिए तैयार हूं।
यदि मैं इसे सही ढंग से समझता हूं, तो आपका प्रश्न दो भागों में है। सबसे पहले, हमले की कमी कम-व्युत्पन्न प्रकार के माध्यम से संरक्षित तरीकों को कॉल करने पर प्रतिबंध को उचित ठहराती है? दूसरा, समान औचित्य समान रूप से व्युत्पन्न या अधिक व्युत्पन्न प्रकारों पर संरक्षित तरीकों से कॉल को रोकने के लिए प्रेरित क्यों नहीं करता है?
पहले भाग स्पष्ट है:
// Good.dll:
public abstract class BankAccount
{
abstract protected void DoTransfer(BankAccount destinationAccount, User authorizedUser, decimal amount);
}
public abstract class SecureBankAccount : BankAccount
{
protected readonly int accountNumber;
public SecureBankAccount(int accountNumber)
{
this.accountNumber = accountNumber;
}
public void Transfer(BankAccount destinationAccount, User user, decimal amount)
{
if (!Authorized(user, accountNumber)) throw something;
this.DoTransfer(destinationAccount, user, amount);
}
}
public sealed class SwissBankAccount : SecureBankAccount
{
public SwissBankAccount(int accountNumber) : base(accountNumber) {}
override protected void DoTransfer(BankAccount destinationAccount, User authorizedUser, decimal amount)
{
// Code to transfer money from a Swiss bank account here.
// This code can assume that authorizedUser is authorized.
// We are guaranteed this because SwissBankAccount is sealed, and
// all callers must go through public version of Transfer from base
// class SecureBankAccount.
}
}
// Evil.exe:
class HostileBankAccount : BankAccount
{
override protected void Transfer(BankAccount destinationAccount, User authorizedUser, decimal amount) { }
public static void Main()
{
User drEvil = new User("Dr. Evil");
BankAccount yours = new SwissBankAccount(1234567);
BankAccount mine = new SwissBankAccount(66666666);
yours.DoTransfer(mine, drEvil, 1000000.00m); // compilation error
// You don't have the right to access the protected member of
// SwissBankAccount just because you are in a
// type derived from BankAccount.
}
}
डॉ बुराई के अपने स्विस बैंक खाते से चोरी करने के लिए एक ... लाख ... डॉलर ... प्रयास सी # संकलक ने नाकाम कर दिया गया है।
स्पष्ट रूप से यह एक मूर्ख उदाहरण है, और जाहिर है, पूरी तरह से भरोसेमंद कोड आपके प्रकार के लिए कुछ भी कर सकता है - पूरी तरह से भरोसेमंद कोड डीबगर शुरू कर सकता है और कोड को इसके चलते बदल सकता है। पूर्ण विश्वास का अर्थ है पूर्ण ट्रस्ट। वास्तव में इस तरह एक असली सुरक्षा प्रणाली डिजाइन नहीं है!
लेकिन मेरा मुद्दा यह है कि यहां "हमला" जो फॉइल किया गया है, कोई है जो सुरक्षित रूप से स्विसबैंक अकाउंट में कोड तक पहुंचने के लिए सिक्योरबैंक अकाउंट द्वारा स्थापित इनवेरिएंट के आसपास एक अंत-दौड़ करने का प्रयास कर रहा है।
यह आपके पहले प्रश्न का उत्तर देता है, मुझे उम्मीद है। यदि यह स्पष्ट नहीं है, तो मुझे बताएं।
आपका दूसरा सवाल यह है कि "सिक्योरबैंक अकाउंट में यह प्रतिबंध क्यों नहीं है?" मेरे उदाहरण में, SecureBankAccount का कहना है:
this.DoTransfer(destinationAccount, user, amount);
जाहिर है "इस" प्रकार SecureBankAccount या कुछ और अधिक व्युत्पन्न की है। यह एक नए स्विसबैंक अकाउंट सहित अधिक व्युत्पन्न प्रकार का कोई भी मूल्य हो सकता है। SecureBankAccount स्विसबैंक अकाउंट के आविष्कारों के आसपास एक अंत-रन नहीं कर सका?
हाँ, बिल्कुल! और इसके कारण, स्विसबैंक अकाउंट के लेखक आवश्यक से समझते हैं कि उनकी बेस क्लास क्या करता है! आप बस कुछ कक्षाओं से निकलने वाले नहीं जा सकते हैं और सर्वश्रेष्ठ के लिए आशा करते हैं! आपके बेस क्लास के कार्यान्वयन को बेस क्लास द्वारा प्रकट संरक्षित तरीकों के सेट को कॉल करने की अनुमति है। यदि आप इससे प्राप्त करना चाहते हैं तो आपको उस वर्ग, या कोड के लिए प्रलेखन को पढ़ने की आवश्यकता है, और समझें कि आपकी संरक्षित विधियों को किस परिस्थिति में बुलाया जाएगा, और तदनुसार अपना कोड लिखें। व्युत्पन्न कार्यान्वयन विवरण साझा करने का एक तरीका है; यदि आप उस चीज़ के कार्यान्वयन विवरण को नहीं समझते हैं जो आप प्राप्त कर रहे हैं तो उससे प्राप्त न करें।
और इसके अलावा, बेस क्लास हमेशा व्युत्पन्न कक्षा से पहले लिखा जाता है। बेस क्लास ऊपर नहीं है और आप पर बदल रहा है, और संभवतः आप कक्षा के लेखक पर भरोसा करते हैं ताकि आप भविष्य के संस्करण के साथ चुपके से तोड़ने का प्रयास न करें। (बेशक, बेस क्लास में बदलाव हमेशा समस्याएं पैदा कर सकता है; यह भंगुर बेस क्लास समस्या का एक और संस्करण है।)
दो मामलों के बीच अंतर यह है कि जब आप बेस क्लास से प्राप्त होते हैं, तो आपके पास समझने और विश्वास करने के लिए एक वर्ग आपकी पसंद का व्यवहार। यह एक व्यावहारिक काम है। स्विसबैंक अकाउंट के लेखकों को यह समझने की आवश्यकता है कि संरक्षित विधि कहने से पहले SecureBankAccount इनवर्तनीय होने की गारंटी देता है। लेकिन उन्हें प्रत्येक संभव चचेरे भाई वर्ग के हर संभावित व्यवहार को समझना और भरोसा नहीं करना चाहिए जो कि समान आधार वर्ग से प्राप्त होता है। उन लोगों को किसी के द्वारा लागू किया जा सकता है और कुछ भी कर सकता है। आपके पास उनके किसी भी प्री-कॉल इनवेरिएंट को समझने की कोई क्षमता नहीं होगी, और इसलिए आपके पास एक कार्यरत संरक्षित विधि को सफलतापूर्वक लिखने की कोई क्षमता नहीं होगी। इसलिए, हम आपको उस परिदृश्य को परेशान और अस्वीकार करते हैं जो आपको बचाते हैं।
और इसके अलावा, हम हैं ताकि आप संभावित रूप से अधिक व्युत्पन्न कक्षाओं के रिसीवर पर संरक्षित तरीकों को कॉल कर सकें। मान लीजिए कि हमने इसे अनुमति नहीं दी और कुछ बेतुका कर दिया। किस परिस्थिति में संरक्षित विधि कभी कहलाया जा सकता है, अगर हम संभावित रूप से अधिक व्युत्पन्न कक्षाओं के रिसीवर पर संरक्षित विधियों को कॉल करने की अनुमति नहीं देते हैं? एकमात्र समय जब आप कभी भी उस दुनिया में संरक्षित विधि को कॉल कर सकते हैं, तो अगर आप अपनी खुद की संरक्षित विधि को एक सीलबंद कक्षा से बुला रहे थे! प्रभावी रूप से, संरक्षित तरीके लगभग कभी कहलाए जा सकते हैं, और जिसे कार्यान्वित किया गया था वह हमेशा सबसे व्युत्पन्न होगा। उस मामले में "संरक्षित" का क्या मतलब है? आपके "संरक्षित" का अर्थ "निजी" जैसा ही है, और इसे केवल एक सीलबंद वर्ग से ही बुलाया जा सकता है। इससे उन्हें कम उपयोगी बना दिया जाएगा।
तो, आपके दोनों प्रश्नों का संक्षिप्त उत्तर "क्योंकि अगर हमने ऐसा नहीं किया है, तो सुरक्षित तरीकों का उपयोग करना असंभव होगा।" हम कम-व्युत्पन्नता के माध्यम से कॉल प्रतिबंधित करते हैं क्योंकि यदि हम नहीं करते हैं, तो सुरक्षित रूप से लागू करना असंभव है जो किसी भी संरक्षित विधि को आविष्कार पर निर्भर करता है। हम संभावित उपप्रकारों के माध्यम से कॉल की अनुमति देते हैं क्योंकि यदि हम इसकी अनुमति नहीं देते हैं, तो हम सभी पर किसी भी कॉल की अनुमति नहीं देते हैं।
क्या यह आपके प्रश्नों का उत्तर देता है?
एरिक लिपर्ट की पोस्ट पढ़ने के बाद, मुझे उत्सुकता है कि उसकी व्याख्या में कमी आई थी। एक स्थिर विश्लेषण में संकलक उस जानकारी को कैसे जान सकता है जिसे आप जानते हैं? मैं उत्सुक हूं कि इस समस्या को हल करने के लिए संविधान या contravariance, इसीलिए यह एक टिप्पणी है और जवाब नहीं है। –
मुझे कोई तरीका नहीं दिखता है कि भिन्नता यहां प्रासंगिक हो जाती है। क्या आप विस्तार कर सकते हैं कि आपको ऐसा क्यों लगता है? –
एरिक का ब्लॉग पोस्ट कहता है, "हम अनग्युलेट को कॉल कर सकते हैं। कानूनी रूप से जिराफ से खाएं, लेकिन हम सुरक्षित विधि ज़ेबरा को नहीं बुला सकते हैं। ज़ेबरा या ज़ेबरा के उप-वर्ग को छोड़कर कुछ भी खाएं।" यह मुझे स्पष्ट नहीं था * क्यों * हम जिराफ को ज़ेबरा को फोन करने से रोकना चाहते हैं। हालांकि, इसे अभी भी SubclassOfGiraffe.Eat पर कॉल करने की इजाजत है। यदि प्रतिबंध वास्तविक प्रकार पर आधारित था ("जिराफ केवल जिराफ के उदाहरणों पर खा सकता है"), मैं संकलक को जानने की अपेक्षा नहीं करता; इसे रनटाइम पर लागू किया जाना होगा। –