GetType () झूठ बोल सकता है?


94

SO में कुछ दिनों पहले पूछे गए निम्नलिखित प्रश्न के आधार पर: GetType () और बहुरूपता और एरिक लिपर्ट के उत्तर को पढ़ते हुए , मैंने यह सोचना शुरू कर दिया कि क्या GetType()आभासी होना वास्तव में सुनिश्चित नहीं है कि कोई वस्तु इसके बारे में झूठ नहीं बोल सकतीType

विशेष रूप से, एरिक का जवाब निम्नलिखित बताता है:

फ्रेमवर्क डिज़ाइनर एक अविश्वसनीय रूप से खतरनाक विशेषता को जोड़ने नहीं जा रहे हैं जैसे कि किसी वस्तु को उसके प्रकार के बारे में झूठ बोलने की अनुमति देना केवल उसी प्रकार के तीन अन्य तरीकों के अनुरूप बनाना।

अब सवाल यह है: मैं एक उद्देश्य यह है कि कर सकते हैं करता है इसे तुरंत स्पष्ट किया जा रहा बिना अपने प्रकार के बारे में झूठ? मैं गहराई से यहाँ गलत हो सकता हूँ और अगर यह मामला है, तो मुझे स्पष्टीकरण पसंद आएगा, लेकिन निम्नलिखित कोड पर विचार करें:

public interface IFoo
{
    Type GetType();
}

और उक्त इंटरफ़ेस के निम्नलिखित दो कार्यान्वयन:

public class BadFoo : IFoo
{
    Type IFoo.GetType()
    {
        return typeof(int);
    }
}

public class NiceFoo : IFoo
{
}

यदि आप निम्न सरल प्रोग्राम चलाते हैं:

static void Main(string[] args)
{
    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    Console.ReadLine();
}

यकीन है कि पर्याप्त badFooएक गलत आउटपुट देता है Type

अब मुझे नहीं पता कि क्या एरिक के इस व्यवहार को " अविश्वसनीय रूप से खतरनाक विशेषता " के रूप में बताने के आधार पर इसका कोई गंभीर प्रभाव है , लेकिन क्या यह पैटर्न एक विश्वसनीय खतरा पैदा कर सकता है?


3
दिलचस्प शीर्षक और विषय!
डेविड

43
IFoo.GetTypeऔर object.GetTypeएक ही बात नहीं है इसलिए खराब शैली के अलावा यहां कुछ भी बुरा नहीं हो रहा है। संपादित करें: आम तौर GetTypeपर कुछ मामलों में संकलित समय पर नहीं जाना जाता है, ज्यादातर मामलों में objectऔर कुछ डॉगी इंटरफ़ेस नहीं कहा जाएगा। :)
लेप्पी

4
आपका शीर्षक महोदय, मेरा दिन बना।
सोनर ग्नूएल

5
आप बस एक ही हस्ताक्षर के साथ नए सदस्यों को भी बुलाते हैं GetType। यह उस GetTypeविधि से संबंधित नहीं है जो महत्वपूर्ण है। आप एक सार्वजनिक उदाहरण विधि भी बना सकते हैं, जो संबंधित GetTypeपद्धति को newसंशोधित करती है , संशोधक कीवर्ड का उपयोग करके । ध्यान दें, यदि आपके पास एक सामान्य विधि है जैसे static Type Test<T>(T t) { return t.GetType(); }(बिना किसी बाधा के T), तो सामान जैसी सामग्री Test<IFoo>(new BadFoo())अभी भी मूल GetTypeविधि कहेगी।
जेपी स्टिग नीलसन

2
@ जैमीक - सवाल "क्या यह पैटर्न एक विश्वसनीय खतरा पैदा कर सकता है?" बयानबाजी नहीं है।
मार्टिन स्मिथ

जवाबों:


45

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

आपने जो कुछ किया है, GetType को छायांकित करने के लिए इस प्रकार है:

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}

इस वर्ग के साथ (और GetType () विधि के लिए MSDN से नमूना कोड का उपयोग करके ) आपके पास वास्तव में हो सकता है:

int n1 = 12;
BadFoo foo = new BadFoo();

Console.WriteLine("n1 and n2 are the same type: {0}",
                  Object.ReferenceEquals(n1.GetType(), foo.GetType())); 
// output: 
// n1 and n2 are the same type: True

तो, हाँ, आपने सफलतापूर्वक झूठ बोला है, है ना? खैर, हां और नहीं ... इस बात पर विचार करें कि इसे एक शोषण के रूप में उपयोग करने का मतलब होगा कि आपके बैडफू उदाहरण का उपयोग कहीं न कहीं एक तर्क के रूप में किया जाएगा, जो कि objectवस्तुओं के पदानुक्रम के लिए या एक सामान्य आधार प्रकार की अपेक्षा करता है। कुछ इस तरह:

public void CheckIfInt(object ob)
{
    if(ob.GetType() == typeof(int))
    {
        Console.WriteLine("got an int! Initiate destruction of Universe!");
    }
    else
    {
        Console.WriteLine("not an int");
    }
}

लेकिन CheckIfInt(foo)प्रिंट "एक इंट नहीं"।

तो, मूल रूप से (आपके उदाहरण पर वापस), आप वास्तव में केवल कोड के साथ अपने "झूठ बोलने के प्रकार" का शोषण कर सकते हैं जो किसी ने आपके IFooइंटरफ़ेस के खिलाफ लिखा था , जो इस तथ्य के बारे में बहुत स्पष्ट है कि इसमें "कस्टम" GetType()विधि है।

केवल अगर GetType () ऑब्जेक्ट पर वर्चुअल था, तो आप एक "झूठ बोल" प्रकार को शिल्प करने में सक्षम होंगे CheckIfIntजो किसी अन्य द्वारा लिखित पुस्तकालयों में कहर बनाने के लिए ऊपर की विधियों के साथ उपयोग किया जा सकता है।


हाँ, इसकी छायांकन के समान ही है। अंतिम पैराग्राफ वास्तव में यह स्पष्ट करता है कि वास्तव में कोई खतरा नहीं है। धन्यवाद!
13:30

32

प्रकार के बारे में सुनिश्चित होने के दो तरीके हैं:

  1. typeofउस प्रकार का उपयोग करें जिसे अतिभारित नहीं किया जा सकता है

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", typeof(BadFoo));
    Console.WriteLine("NiceFoo really is a '{0}'", typeof(NiceFoo));
    Console.ReadLine();
  2. उदाहरण के लिए objectऔर GetType()विधि को कॉल करें

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", ((object)badFoo).GetType());
    Console.WriteLine("NiceFoo really is a '{0}'", ((object)niceFoo).GetType());
    Console.ReadLine();

1
आप typeofएक ऐसी विधि का उपयोग कैसे करेंगे जो केवल एक IFoo badFooपैरामीटर के रूप में मिलती है ?
huysentruitw

typeofएक वर्ग के उदाहरणों पर लागू नहीं किया जा सकता है जो हमें यहां करने की आवश्यकता है। आपका ही एकमात्र विकल्प है GetType()
inbetween

आपके दो नमूने दो अलग चीजें कर रहे हैं। दूसरी पंक्तियों के लिए आवश्यक प्रश्न का उत्तर नहीं है - वे स्पष्ट रूप से "प्रकार प्राप्त करते हैं BadFoo" लेकिन वे "चर का प्रकार नहीं प्राप्त करते हैं badFoo।"
डैन पूजे

हाँ मुझे क्षमा करें। हमेशा की तरह मैंने प्रश्न को ध्यान से नहीं पढ़ा। मैंने टाइप के बारे में सुनिश्चित होने के दो अलग-अलग तरीकों को इंगित करने के लिए अपना उत्तर अपडेट किया।
जोहान्स वेंजेक

1
यह वही है जो मैं इंगित कर रहा था। तो आपकी टिप्पणी का क्या मतलब है? :)
जोहान्स वानज़ेक

10

नहीं, आप GetType झूठ नहीं बना सकते। आप केवल एक नई विधि शुरू कर रहे हैं। केवल कोड जो इस पद्धति के बारे में जानते हैं, उसे कॉल करेंगे।

आप उदाहरण के लिए तृतीय-पक्ष या फ्रेमवर्क कोड को वास्तविक के बजाय अपना नया गेट टाइप तरीका नहीं बना सकते हैं, क्योंकि उस कोड को पता नहीं है कि आपका तरीका मौजूद है और इसलिए इसे कभी भी कॉल नहीं किया जाएगा।

आप इस तरह की घोषणा के साथ अपने स्वयं के डेवलपर्स को भ्रमित कर सकते हैं। कोई भी कोड जो आपके घोषणा पत्र के साथ संकलित किया गया है और जो कि IFoo के रूप में टाइप किए गए मापदंडों या चर का उपयोग करता है या उससे लिया गया कोई भी प्रकार वास्तव में आपके नए तरीके का उपयोग करेगा। लेकिन चूंकि यह केवल आपके स्वयं के कोड को प्रभावित करता है, इसलिए यह वास्तव में "खतरा" नहीं है।

यदि आप एक वर्ग के लिए एक कस्टम प्रकार का विवरण प्रदान करना चाहते हैं, तो यह एक कस्टम प्रकार Descriptor का उपयोग करके किया जाना चाहिए , शायद एक TypeDescriptionProviderAttribute के साथ अपनी कक्षा को एनोटेट करके । यह कुछ स्थितियों में उपयोगी हो सकता है।


2
अपने दूसरे पैराग्राफ के लिए +1 स्पष्ट रूप से इंगित करता है कि 3-पार्टी कोड एक कस्टम गेट टाइप कार्यान्वयन के बारे में नहीं जानता है। अन्य उत्तर उस विचार की ओर संकेत करते थे लेकिन वास्तव में सामने नहीं आए और यह कहा (कम से कम स्पष्ट रूप से नहीं)।
ब्रिचिन

7

खैर, वास्तव में पहले से ही एक प्रकार है जो अंदर झूठ बोल सकता हैGetType : किसी भी अशक्त प्रकार।

यह कोड :

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());

आउटपुट True


दरअसल, यह int?झूठ नहीं है, बस निहित कलाकारों को एक बॉक्सिंग में objectबदल int?जाता है int। लेकिन फिर भी आप नहीं कर सकते हैं बताओ int?से intसाथ GetType()


1
जो अपेक्षित (या कम से कम प्रसिद्ध) व्यवहार है। इस प्रश्न के उत्तर इस अवधारणा को बहुत स्पष्ट रूप से बताते हैं, साथ ही इसका कारण भी है।
ब्रिचिन

@brichins: ठीक है, मैं मानता हूँ कि यह ज्ञात है, लेकिन मैं सहमत नहीं हूँ यह अच्छी तरह से जाना जाता है। वैसे भी, यह एक ऐसा मामला है जहां GetType()कुछ अजीब परिणाम उत्पन्न होता है। मैंने वास्तव में कई सहयोगियों से पूछा है कि क्या कोई गैर-छायांकित GetType()कुछ वापस कर सकता है जो वास्तविक वस्तु के रनटाइम प्रकार से भिन्न होता है, सभी का जवाब 'नहीं' था।
व्लाद

5

मुझे नहीं लगता कि यह होगा, क्योंकि प्रत्येक लाइब्रेरी कोड जो गेटटाइप को कॉल करता है, वह चर को 'ऑब्जेक्ट' के रूप में या जेनेरिक '' '' के रूप में घोषित करेगा।

निम्नलिखित कोड:

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintObjectType("BadFoo", badFoo);
        PrintObjectType("NiceFoo", niceFoo);
        PrintGenericType("BadFoo", badFoo);
        PrintGenericType("NiceFoo", niceFoo);
    }

    public static void PrintObjectType(string actualName, object instance)
    {
        Console.WriteLine("Object {0} says he's a '{1}'", actualName, instance.GetType());
    }

    public static void PrintGenericType<T>(string actualName, T instance)
    {
        Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType());
    }

प्रिंट:

ऑब्जेक्ट बैडफू का कहना है कि वह एक 'टाइपकोनैप्ट.बेडफू' है

ऑब्जेक्ट नाइसफू का कहना है कि वह एक 'टाइपकॉनसेप्ट.नीसेफू' है

जेनेरिक टाइप बैडफू कहते हैं कि वह एक 'टाइपकोनैप्ट.बेडफू' है

जेनेरिक टाइप नाइसफू कहता है कि वह एक 'टाइपकॉन्सेप्ट.नीसेफू' है

केवल इस तरह के कोड के परिणामस्वरूप बुरे परिदृश्य का परिणाम आपके स्वयं के कोड में होगा, जहां आप पैरामीटर प्रकार को IFoo घोषित करते हैं

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintIFoo("BadFoo", badFoo);
        PrintIFoo("NiceFoo", niceFoo);
    }

    public static void PrintIFoo(string actualName, IFoo instance)
    {
        Console.WriteLine("IFoo {0} says he's a '{1}'", actualName, instance.GetType());
    }

IFoo BadFoo का कहना है कि वह एक 'System.Int32' है

इफू नाइसफू का कहना है कि वह एक 'टाइपकॉनसेप्ट.नीसेफू' है


4

सबसे बुरी बात यह है कि जहाँ तक मैं बता सकता हूँ भ्रामक प्रोग्रामर हैं जो जहर की क्लास का उपयोग करते हैं, उदाहरण के लिए:

Type type = myInstance.GetType();
string fullName = type.FullName;
string output;
if (fullName.Contains(".Web"))
{
    output = "this is webby";
}
else if (fullName.Contains(".Customer"))
{
    output = "this is customer related class";
}
else
{
    output = "unknown class";
}

यदि myInstanceकिसी वर्ग का उदाहरण है जैसे कि आप प्रश्न में वर्णन करते हैं, तो इसे केवल अज्ञात प्रकार माना जाएगा।

तो मेरा जवाब नहीं है, यहाँ कोई वास्तविक खतरा नहीं देख सकता।


1
ज़रूर। एक सावधान प्रोग्रामर संकलन-समय पर देख सकता है कि वह किस विधि "GetType" को आमंत्रित करता है। Object.GetType()से अलग है SomeUserdefinedInterfaceClassOrStruct.GetType()। केवल, यदि आप dynamicप्रकार का उपयोग करते हैं, तो आप कभी नहीं जान सकते कि बाध्यकारी समय पर क्या होगा। तो आपको dynamic x = expression; ... Type t = ((object)x).GetType();ऐसे मामलों में उपयोग करना चाहिए ।
जेपी स्टिग नीलसन

@Jeppe निष्पक्ष अंक! मुझे लगता है कि यह अलग जवाब को सही ठहराता है, मेरा जवाब "निर्दोष" प्रोग्रामर पर अधिक ध्यान केंद्रित करता है जो इतना सावधान नहीं होगा।
शैडो विजार्ड ईयर फॉर यू

3

आपके पास कुछ विकल्प हैं यदि आप उस तरह के हैक के खिलाफ सुरक्षित खेलना चाहते हैं:

पहले ऑब्जेक्ट पर कास्ट करें

आप GetType()पहले उदाहरण के लिए मूल विधि से कॉल कर सकते हैं object:

 Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());

का परिणाम:

BadFoo says he's a 'ConsoleApplication.BadFoo'

टेम्प्लेट विधि का उपयोग करें

इस टेम्पलेट विधि का उपयोग करने से आपको वास्तविक प्रकार भी मिलेगा:

static Type GetType<T>(T obj)
{
    return obj.GetType();
}

GetType(badFoo);

2

object.GetTypeऔर के बीच अंतर है IFoo.GetTypeGetTypeनहीं जाना जाता वस्तुओं पर संकलन समय पर, इंटरफेस पर नहीं कहा जाता है। अपने उदाहरण में, आउटपुट के साथbadFoo.GetType यह bahaviour अपेक्षित है, क्योंकि आप विधि को अधिभारित करते हैं। केवल बात यह है, कि अन्य प्रोग्रामर इस व्यवहार से भ्रमित हो सकते हैं।

लेकिन यदि आप इसका उपयोग करते हैं typeof()तो यह आउटपुट करेगा कि प्रकार समान है, और आप ओवरराइट नहीं कर सकतेtypeof()

साथ ही प्रोग्रामर कंपाइल टाइम पर देख सकता है कि कौन सा तरीका है GetType वह को लागू करता है।

तो आपके प्रश्न के लिए: यह पैटर्न एक विश्वसनीय खतरा पैदा नहीं कर सकता है, लेकिन यह कोडिंग की सबसे अच्छी शैली भी नहीं है।


badFoo.GetType()आईएस ने व्यवहार की उम्मीद की GetTypeथी , क्योंकि अतिभारित था।
huysentruitw
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.