CLR में 'as' कीवर्ड का उपयोग करके कास्टिंग बनाम


386

जब प्रोग्रामिंग इंटरफेस, मैंने पाया है कि मैं बहुत अधिक कास्टिंग या ऑब्जेक्ट टाइप रूपांतरण कर रहा हूं।

क्या रूपांतरण के इन दो तरीकों में अंतर है? यदि हां, तो क्या लागत में अंतर है या यह मेरे कार्यक्रम को कैसे प्रभावित करता है?

public interface IMyInterface
{
    void AMethod();
}

public class MyClass : IMyInterface
{
    public void AMethod()
    {
       //Do work
    }

    // Other helper methods....
}

public class Implementation
{
    IMyInterface _MyObj;
    MyClass _myCls1;
    MyClass _myCls2;

    public Implementation()
    {
        _MyObj = new MyClass();

        // What is the difference here:
        _myCls1 = (MyClass)_MyObj;
        _myCls2 = (_MyObj as MyClass);
    }
}

इसके अलावा, पसंदीदा विधि "सामान्य" क्या है?


क्या आप इस बात का एक छोटा सा उदाहरण जोड़ सकते हैं कि आप पहले सवाल में जातियों का उपयोग क्यों कर रहे हैं, या शायद एक नई शुरुआत करें? मुझे इस बात में दिलचस्पी है कि आपको इकाई परीक्षण के लिए कलाकारों की आवश्यकता क्यों होगी। मुझे लगता है कि यह इस सवाल के दायरे से बाहर है, हालांकि।
एरिक वैन ब्राकेल

2
मैं शायद इसके लिए इस आवश्यकता को रोकने के लिए अपने यूनिट टेस्ट को बदल सकता हूं। मूल रूप से यह इस तथ्य से उबलता है कि मेरे पास मेरी ठोस वस्तु पर एक संपत्ति है जो इंटरफ़ेस में नहीं है। मुझे उस संपत्ति को स्थापित करने की आवश्यकता है लेकिन वास्तविक जीवन उस संपत्ति को अन्य तरीकों से निर्धारित किया गया है। क्या वह आपके सवाल का जवाब है?
फ्रैंक वी

जैसा कि पैट्रिक हैगने ने कहा है कि नीचे अंतर है, एक अंतर है।
नील

जवाबों:


517

रेखा के नीचे उत्तर 2008 में लिखा गया था।

C # 7 पेश किया गया पैटर्न मिलान, जो मोटे तौर पर asऑपरेटर को बदल दिया है , जैसा कि आप अब लिख सकते हैं:

if (randomObject is TargetType tt)
{
    // Use tt here
}

ध्यान दें कि ttइसके बाद भी गुंजाइश है, लेकिन निश्चित रूप से असाइन नहीं किया गया है। (यह है निश्चित रूप से भीतर सौंपा ifशरीर।) यही कारण है कि कुछ मामलों में थोड़ा कष्टप्रद है, इसलिए यदि आप वास्तव में हर दायरे में संभव चर की सबसे छोटी संख्या को शुरू करने के बारे में परवाह है, तो आप अभी भी उपयोग कर सकते हैं isएक डाली गई।


मुझे नहीं लगता कि अब तक कोई भी उत्तर (इस उत्तर को शुरू करने के समय पर!) ने वास्तव में समझाया है कि यह कहां उपयोग करने लायक है।

  • ऐसा न करें:

    // Bad code - checks type twice for no reason
    if (randomObject is TargetType)
    {
        TargetType foo = (TargetType) randomObject;
        // Do something with foo
    }

    न केवल यह दो बार जाँच कर रहा है, लेकिन यह randomObjectस्थानीय चर के बजाय एक क्षेत्र है , तो विभिन्न चीजों की जाँच हो सकती है । "यदि" पास करना संभव है, लेकिन फिर असफल होना है, अगर एक और धागा randomObjectदोनों के बीच के मूल्य को बदलता है ।

  • यदि randomObjectवास्तव में इसका उदाहरण होना चाहिएTargetType , यदि ऐसा नहीं है, तो इसका मतलब है कि बग है, तो कास्टिंग सही समाधान है। यह एक अपवाद को तुरंत फेंक देता है, जिसका अर्थ है कि कोई भी काम गलत धारणाओं के तहत नहीं किया गया है, और अपवाद सही ढंग से बग के प्रकार को दर्शाता है।

    // This will throw an exception if randomObject is non-null and
    // refers to an object of an incompatible type. The cast is
    // the best code if that's the behaviour you want.
    TargetType convertedRandomObject = (TargetType) randomObject;
  • यदि इसका उदाहरण randomObject हो सकता है TargetTypeऔर TargetTypeयह एक संदर्भ प्रकार है, तो इस तरह कोड का उपयोग करें:

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject
    }
  • यदि इसका एक उदाहरण randomObject हो सकता है TargetTypeऔर TargetTypeएक मूल्य प्रकार है, तो हम स्वयं के asसाथ उपयोग नहीं कर सकते TargetType, लेकिन हम एक अशक्त प्रकार का उपयोग कर सकते हैं:

    TargetType? convertedRandomObject = randomObject as TargetType?;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject.Value
    }

    (नोट: वर्तमान में यह वास्तव में + कलाकारों की तुलना में धीमा है । मुझे लगता है कि यह अधिक सुरुचिपूर्ण और सुसंगत है, लेकिन वहां हम जाते हैं।)

  • यदि आपको वास्तव में परिवर्तित मूल्य की आवश्यकता नहीं है, लेकिन आपको यह जानने की जरूरत है कि क्या यह टारगेट का एक उदाहरण है, तो isऑपरेटर आपका दोस्त है। इस मामले में यह मायने नहीं रखता है कि टारगेट टाइप एक संदर्भ प्रकार है या एक मूल्य प्रकार है।

  • जेनरिक से जुड़े अन्य मामले हो सकते हैं, जहां isउपयोगी है (क्योंकि आप नहीं जानते हैं कि टी एक संदर्भ प्रकार है या नहीं, इसलिए आप इसका उपयोग नहीं कर सकते हैं) लेकिन वे अपेक्षाकृत अस्पष्ट हैं।

  • मैंने अब से पहले निश्चित रूप isसे मूल्य प्रकार के मामले के लिए उपयोग किया है, एक अशक्त प्रकार और asएक साथ उपयोग करने के बारे में नहीं सोचा है :)


संपादित करें: ध्यान दें कि उपरोक्त में से कोई भी प्रदर्शन के बारे में बात नहीं करता है, मूल्य प्रकार के मामले के अलावा, जहां मैंने नोट किया है कि एक अशक्त मूल्य प्रकार के लिए अनबॉक्सिंग वास्तव में धीमा है - लेकिन सुसंगत।

नासाकिंग के उत्तर के अनुसार, और-कास्ट या इज़-एंड-जैसे दोनों आधुनिक जेआईटी के साथ उतने ही अशक्त हैं, जितना कि नीचे दिए गए कोड द्वारा दिखाया गया है:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "x";
            values[i + 2] = new object();
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);
    }

    static void FindLengthWithIsAndCast(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string) o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

मेरे लैपटॉप पर, ये सभी लगभग 60ms में निष्पादित होते हैं। ध्यान देने योग्य दो बातें:

  • उनके बीच कोई महत्वपूर्ण अंतर नहीं है। (वास्तव में, ऐसी परिस्थितियां हैं जिनमें एज़-प्लस-नल-चेक निश्चित रूप से धीमा है। उपरोक्त कोड वास्तव में टाइप चेक को आसान बनाता है क्योंकि यह एक सील वर्ग के लिए है; यदि आप एक इंटरफ़ेस के लिए जाँच कर रहे हैं, तो संतुलन थोड़ा सुझाव देता है। as-plus-null-check के पक्ष में।)
  • वे सब बहुत तेजी से कर रहे हैं । यह केवल आपके कोड में अड़चन नहीं होगा जब तक आप वास्तव में मूल्यों के साथ कुछ भी नहीं करने जा रहे हैं ।

तो चलो प्रदर्शन के बारे में चिंता मत करो। चलो शुद्धता और स्थिरता के बारे में चिंता करते हैं।

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

मैं यह भी बनाए रखता हूं कि जब-तब-शून्य-जांच चिंताओं को बेहतर ढंग से अलग करती है। हमारे पास एक कथन है जो रूपांतरण का प्रयास करता है, और फिर एक कथन जो परिणाम का उपयोग करता है। Is-and-cast या is-and-as एक परीक्षण करता है और फिर मूल्य बदलने के लिए एक और प्रयास।

इसे दूसरे तरीके से रखने के लिए, क्या कोई कभी लिख पाएगा :

int value;
if (int.TryParse(text, out value))
{
    value = int.Parse(text);
    // Use value
}

यह इस प्रकार है कि क्या-और-कास्ट कर रहा है - हालांकि स्पष्ट रूप से एक सस्ता तरीके से।


7
यहाँ लागत है की है / के रूप में / आईएल के मामले में कास्टिंग: atalasoft.com/cs/blogs/stevehawley/archive/2009/01/30/...
कुर्सी

3
मामले में, यदि targetObject टारगेट टाइप हो सकता है , तो "is" और कास्ट कॉम्बिनेशन का उपयोग करना एक बुरा अभ्यास क्यों माना जाता है? मेरा मतलब है, यह एक धीमी कोड उत्पन्न करता है, लेकिन इस मामले में इरादे एएस के कलाकारों की तुलना में अधिक स्पष्ट हैं, जैसे "कुछ करो अगर targetObject targetType है", इसके बजाय "Do if if targetObject null है" तो इसके अलावा, AS AS एक अनावश्यक चर बनाएगा IF स्कोप से बाहर।
वलेरा कोलुपाएव

2
@ वलेरा: अच्छे अंक, हालांकि मैं सुझाव दूंगा कि जैसा / अशक्त परीक्षण पर्याप्त रूप से मुहावरेदार है कि इरादा लगभग सभी सी # डेवलपर्स के लिए स्पष्ट होना चाहिए। मुझे व्यक्तिगत रूप से + कास्ट में शामिल दोहराव पसंद नहीं है। मैं वास्तव में एक प्रकार का "जैसा-अगर" निर्माण करना चाहता हूं, जो दोनों कार्यों को एक में करता है। वे इतनी बार एक साथ जाते हैं ...
जॉन स्कीट

2
@ जोंन स्कीट: माई लेट। एस एंड कास्ट: २१३५, इज़ एंड अस: २१४५, अस एंड नल चेक: १ ९ ६१, स्पेक्स: ओएस: विंडोज सेवन, सीपीयू: आई ५-५२० एम, ४ जीबी ऑफ़ डीडीआर १०३३ रे, बेंचमार्क ऑन अरेंज 128,000,000 वस्तुओं की।
बेहरोज़

2
C # 7 के साथ आप कर सकते हैं: if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}या डॉक्स का उपयोग switch/ case देख
WerWet

71

"के रूप में" यदि संभव नहीं है तो पूरी तरह से वापस आ जाएगी।

इससे पहले कास्टिंग एक अपवाद बढ़ाएगा।

प्रदर्शन के लिए, एक अपवाद उठाना आमतौर पर समय में अधिक महंगा होता है।


3
अपवाद को ऊपर उठाने अधिक महंगा है, लेकिन अगर आप जानते हैं कि वस्तु सही ढंग से डाली जा सकती है, के रूप में (एंटोन की प्रतिक्रिया देखें) सुरक्षा जांच की वजह से अधिक समय की आवश्यकता है। हालांकि, सुरक्षा जांच की लागत है, मेरा मानना ​​है कि काफी छोटा है।

17
संभावित रूप से एक अपवाद को बढ़ाने की लागत पर विचार करने के लिए एक कारक है, लेकिन यह अक्सर सही डिजाइन है।
जेफरी एल व्हिटलेज

@panesofglass - संदर्भ प्रकारों के लिए, रूपांतरण संगतता हमेशा के रूप में और कलाकारों दोनों के लिए रन-टाइम पर जाँच की जाएगी, ताकि कारक दोनों विकल्पों के बीच अंतर न करें। (यदि ऐसा नहीं होता, तो कास्ट एक अपवाद नहीं उठा सकता।)
जेफरी एल व्हिटलेज

4
@ फ्रेंक - यदि आपको पूर्व-जेनरिक संग्रह का उपयोग करने की आवश्यकता है, उदाहरण के लिए, और आपके एपीआई में एक विधि को कर्मचारियों की सूची की आवश्यकता होती है, और कुछ जोकर इसके बजाय उत्पादों की एक सूची से गुजरते हैं, तो एक अमान्य कास्ट अपवाद सिग्नल के लिए उपयुक्त हो सकता है इंटरफ़ेस आवश्यकताओं का उल्लंघन।
जेफरी एल व्हाईटलेज

27

यहाँ कुछ IL तुलना के साथ एक और उत्तर दिया गया है। कक्षा पर विचार करें:

public class MyClass
{
    public static void Main()
    {
        // Call the 2 methods
    }

    public void DirectCast(Object obj)
    {
        if ( obj is MyClass)
        { 
            MyClass myclass = (MyClass) obj; 
            Console.WriteLine(obj);
        } 
    } 


    public void UsesAs(object obj) 
    { 
        MyClass myclass = obj as MyClass; 
        if (myclass != null) 
        { 
            Console.WriteLine(obj);
        } 
    }
}

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

.method public hidebysig instance void  DirectCast(object obj) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  brfalse.s  IL_0015
  IL_0008:  ldarg.1
  IL_0009:  castclass  MyClass
  IL_000e:  pop
  IL_000f:  ldarg.1
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0015:  ret
} // end of method MyClass::DirectCast

.method public hidebysig instance void  UsesAs(object obj) cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (class MyClass V_0)
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  brfalse.s  IL_0010
  IL_000a:  ldarg.1
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0010:  ret
} // end of method MyClass::UsesAs

आइंस्टीन कीवर्ड बनाम कास्टक्लास

इस ब्लॉग पोस्ट में इसे करने के दो तरीकों के बीच एक सभ्य तुलना है। उनका सारांश है:

  • एक सीधी तुलना में, आइंस्टीन कास्टक्लास से तेज है (हालांकि केवल थोड़ा सा)
  • जब रूपांतरण सुनिश्चित करने के लिए जाँच करना सफल रहा, तो आइंस्टीन कास्टक्लास की तुलना में काफी तेज था
  • आइंस्टीन और कास्टक्लास के संयोजन का उपयोग नहीं किया जाना चाहिए क्योंकि यह सबसे तेज "सुरक्षित" रूपांतरण (12% से अधिक धीमा) की तुलना में धीमा था

मैं व्यक्तिगत रूप से हमेशा अस का उपयोग करता हूं, क्योंकि यह पढ़ने में आसान है और .NET विकास टीम (या जेफरी रिक्टर) द्वारा अनुशंसित है


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

18

दोनों के बीच एक और अधिक सूक्ष्म अंतर यह है कि "के रूप में" कास्ट कास्टिंग के लिए उपयोग नहीं किया जा सकता है जब एक संचालक शामिल होता है:

public class Foo
{
    public string Value;

    public static explicit operator string(Foo f)
    {
        return f.Value;
    }

}

public class Example
{
    public void Convert()
    {
        var f = new Foo();
        f.Value = "abc";

        string cast = (string)f;
        string tryCast = f as string;
    }
}

यह संकलित नहीं करेगा (हालांकि मुझे लगता है कि पिछले संस्करणों में ऐसा किया था) चूंकि "के रूप में" कीवर्ड संचालकों को ध्यान में नहीं रखते हैं। लाइन string cast = (string)f;हालांकि ठीक काम करता है।


12

जैसा कि एक अपवाद नहीं फेंकता है अगर यह बदले में अशक्त रिटर्न नहीं दे सकता ( जैसा कि केवल संदर्भ प्रकारों पर संचालित होता है)। इसलिए मूल रूप से इसका उपयोग करना बराबर है

_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;

दूसरी ओर, सी-स्टाइल कास्ट, एक अपवाद फेंकते हैं जब कोई रूपांतरण संभव नहीं होता है।


4
समतुल्य, हाँ, लेकिन समान नहीं। यह तब और अधिक कोड उत्पन्न करता है।
१०:१४

10

वास्तव में आपके प्रश्न का उत्तर नहीं है, लेकिन मुझे लगता है कि एक महत्वपूर्ण संबंधित बिंदु है।

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


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

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

@ TheSenator यह प्रश्न 3 साल से अधिक पुराना है इसलिए मुझे वास्तव में याद नहीं है। लेकिन मैं शायद आक्रामक रूप से यूनिट परीक्षण के दौरान भी इंटरफेस का उपयोग कर रहा था। संभवतः क्योंकि मैं कारखाने के पैटर्न का उपयोग कर रहा था और परीक्षण करने के लिए लक्ष्य वस्तुओं पर एक सार्वजनिक निर्माणकर्ता तक पहुंच नहीं थी।
फ्रैंक वी

9

कृपया जॉन स्कीट की सलाह को अनदेखा करें, पुन: परीक्षण-और-कास्ट पैटर्न से बचें, अर्थात:

if (randomObject is TargetType)
{
    TargetType foo = randomObject as TargetType;
    // Do something with foo
}

यह विचार कि यह एक कास्ट और एक अशक्त परीक्षण से अधिक लागत है, एक मिथक है :

TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
    // Do stuff with convertedRandomObject
}

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

यदि आप एक कारण चाहते हैं कि टेस्ट-एंड-कास्ट तेज़ है, या कम से कम धीमा नहीं है, तो एक सरल और जटिल कारण है।

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

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

तो कृपया, कृपया इस "कास्ट-एंड-नल-टेस्ट टेस्ट-एंड-कास्ट" सलाह डीआईई से बेहतर है। कृप्या। परीक्षण और कास्ट सुरक्षित और तेज दोनों है।


7
@ स्नैस्किंग: यदि आप दो बार (अपने पहले स्निपेट के अनुसार) परीक्षण करते हैं, तो एक मौका है कि दो परीक्षणों के बीच प्रकार बदल जाएगा, अगर यह एक क्षेत्र या refपैरामीटर है। यह स्थानीय चर के लिए सुरक्षित है, लेकिन खेतों के लिए नहीं। मुझे आपके बेंचमार्क चलाने में दिलचस्पी होगी, लेकिन आपने अपने ब्लॉग पोस्ट में जो कोड दिया है, वह पूरा नहीं है। मैं माइक्रो-ऑप्टिमाइज़िंग नहीं करने से सहमत हूं, लेकिन मुझे नहीं लगता कि दो बार मूल्य का उपयोग करना "अधिक" और एक अशक्तता परीक्षण की तुलना में अधिक पठनीय या सुरुचिपूर्ण है। (मैं निश्चित रूप से एक के बाद "के रूप में" के बजाय एक सीधे कलाकारों का उपयोग करेगा, एक btw के बाद।)
जॉन स्कीट

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

1
+1 यह इंगित करने के लिए है कि / कास्ट या के रूप में / कास्ट वास्तविकता में कोई धीमा है, आप पर ध्यान दें। स्वयं एक पूर्ण परीक्षण चलाने के बाद, मैं यह पुष्टि कर सकता हूं कि जहां तक ​​मैं देख सकता हूं, इससे कोई अंतर नहीं पड़ता - और स्पष्ट रूप से आप बहुत कम समय में जातियों की संख्या को बढ़ा सकते हैं। मेरे जवाब को पूर्ण कोड के साथ अपडेट करेंगे।
जॉन स्कीट

1
वास्तव में, यदि बंधन स्थानीय नहीं है, तो एक TOCTTOU बग (समय-समय-चेक-टू-टाइम-उपयोग) का मौका है, इसलिए वहां अच्छा बिंदु है। क्यों यह सुरक्षित है के लिए, मैं बहुत से जूनियर डेवलपर्स के साथ काम करता हूं जो किसी कारण से स्थानीय लोगों का पुन: उपयोग करना पसंद करते हैं। इस प्रकार मेरे अनुभव में कास्ट-एंड-नल बहुत वास्तविक खतरा है, और मैंने कभी भी TOCTTOU स्थिति में नहीं चला है क्योंकि मैं अपने कोड को इस तरह से डिज़ाइन नहीं करता हूं। रनटाइम टेस्ट गति के लिए, यह आभासी प्रेषण [1] से भी तेज है! पुन: कोड, मैं देखूंगा कि क्या मैं कास्ट टेस्ट के लिए स्रोत पा सकता हूं। [१] हायरलॉगिक्स .blogspot.com
2008

1
@naasking: मैं स्थानीय पुन: उपयोग की समस्या में कभी नहीं गया - लेकिन मैं कहूँगा कि कोड की समीक्षा में स्पॉट करने के लिए अधिक सूक्ष्म TOCTTOU बग से आसान है। यह भी इंगित करने योग्य है कि मैंने एक सील वर्ग के बजाय इंटरफेस के लिए अपने स्वयं के बेंचमार्क चेकिंग को फिर से चलाया है, और यह कि तब-के-नल-चेक के पक्ष में प्रदर्शन को सुझाव देता है ... लेकिन जैसा कि मैंने कहा है, प्रदर्शन isn मैं यहां कोई विशेष दृष्टिकोण क्यों चुनूंगा।
जॉन स्कीट

4

यदि कास्ट विफल हो जाता है, 'के रूप में' कीवर्ड एक अपवाद नहीं फेंकता है; इसके बजाय चर को शून्य (या मान प्रकार के लिए डिफ़ॉल्ट मान) पर सेट करता है।


3
मान प्रकारों के लिए कोई डिफ़ॉल्ट मान नहीं। जैसा कि कास्टिंग प्रकार के मूल्य के लिए उपयोग नहीं किया जा सकता है।
पेट्रिक हेगन

2
'As' कीवर्ड वास्तव में मूल्य प्रकारों पर काम नहीं करता है, इसलिए यह हमेशा शून्य पर सेट होता है।
एरिक वैन ब्रैकेल

4

यह प्रश्न का उत्तर नहीं है, लेकिन प्रश्न के कोड उदाहरण के लिए टिप्पणी करें:

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

यदि आपने MyClass को IMyInterface डाला, तो इससे पहले ही आप मान लें कि आपको MyClass का एक प्रकार मिलता है और IMyInterface का उपयोग करने का कोई मतलब नहीं है, क्योंकि यदि आप IMyInterface को लागू करने वाली अन्य कक्षाओं के साथ अपना कोड फीड करते हैं, तो यह आपके कोड को तोड़ देगा ...

अब, मेरी सलाह: यदि आपके इंटरफेस अच्छी तरह से डिज़ाइन किए गए हैं, तो आप बहुत अधिक टाइपकास्टिंग से बच सकते हैं।


3

asऑपरेटर केवल, संदर्भ प्रकार पर इस्तेमाल किया जा सकता यह अतिभारित नहीं किया जा सकता है, और यह वापस आ जाएगी nullअगर आपरेशन विफल रहता है। यह कभी भी अपवाद नहीं फेंकेगा।

कास्टिंग का उपयोग किसी भी संगत प्रकार पर किया जा सकता है, इसे ओवरलोड किया जा सकता है, और यदि ऑपरेशन विफल हो जाता है, तो यह एक अपवाद फेंक देगा।

किन विकल्पों का उपयोग करना है यह परिस्थितियों पर निर्भर करता है। मुख्य रूप से, यह एक बात है कि क्या आप एक असफल रूपांतरण पर एक अपवाद फेंकना चाहते हैं।


1
used as ’का उपयोग अशक्त मूल्य प्रकारों पर भी किया जा सकता है, जो एक दिलचस्प पैटर्न प्रदान करता है। कोड के लिए मेरा जवाब देखें।
जॉन स्कीट

1

मेरा जवाब केवल उन मामलों में गति के बारे में है जब हम प्रकार की जांच नहीं करते हैं और हम कास्टिंग के बाद नल की जांच नहीं करते हैं। मैंने जॉन स्कीट के कोड में दो अतिरिक्त परीक्षण जोड़े:

using System;
using System.Diagnostics;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];

        for (int i = 0; i < Size; i++)
        {
            values[i] = "x";
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);

        FindLengthWithCast(values);
        FindLengthWithAs(values);

        Console.ReadLine();
    }

    static void FindLengthWithIsAndCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string)o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
    static void FindLengthWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = (string)o;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

परिणाम:

Is and Cast: 30000000 : 88
Is and As: 30000000 : 93
As and null check: 30000000 : 56
Cast: 30000000 : 66
As: 30000000 : 46

गति पर ध्यान केंद्रित करने की कोशिश मत करो (जैसा मैंने किया) क्योंकि यह सब बहुत तेज है।


इसी तरह, मेरे परीक्षण में, मैंने पाया कि asरूपांतरण (त्रुटि जाँच के बिना) कास्टिंग की तुलना में लगभग 1-3% तेज़ी से चला (100 मिलियन पुनरावृत्तियों पर 540ms बनाम 550ms के आसपास)। न तो आपका आवेदन बनाएगा और न ही तोड़ेगा।
palswim

1

सभी के अलावा जो पहले से ही यहां उजागर किया गया था, मैं सिर्फ एक व्यावहारिक अंतर के साथ आया था जो मुझे लगता है कि स्पष्ट कास्टिंग के बीच ध्यान देने योग्य है

var x = (T) ...

बनाम asऑपरेटर का उपयोग कर ।

यहाँ उदाहरण है:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GenericCaster<string>(12345));
        Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
        Console.WriteLine(GenericCaster<double>(20.4));

        //prints:
        //12345
        //null
        //20.4

        Console.WriteLine(GenericCaster2<string>(12345));
        Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");

        //will not compile -> 20.4 does not comply due to the type constraint "T : class"
        //Console.WriteLine(GenericCaster2<double>(20.4));
    }

    static T GenericCaster<T>(object value, T defaultValue = default(T))
    {
        T castedValue;
        try
        {
            castedValue = (T) Convert.ChangeType(value, typeof(T));
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }

    static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
    {
        T castedValue;
        try
        {
            castedValue = Convert.ChangeType(value, typeof(T)) as T;
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }
}

नीचे पंक्ति: GenericCaster2 संरचनात्मक प्रकारों के साथ काम नहीं करेगा। GenericCaster करेगा।


1

यदि आप .NET फ्रेमवर्क 4.X को लक्षित करने वाले Office PIA का उपयोग करते हैं , तो आपको कीवर्ड के रूप में उपयोग करना चाहिए , अन्यथा यह संकलन नहीं करेगा।

Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;

हालांकि .NET 2.0 को लक्षित करते समय कास्टिंग ठीक है:

Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);

.NET 4.X को लक्षित करते समय त्रुटियाँ हैं:

त्रुटि CS0656: लापता कंपाइलर के लिए आवश्यक सदस्य 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

त्रुटि CS0656: लापता संकलक आवश्यक सदस्य 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'


0

asकीवर्ड बड़ा अंतर है, तो रूपांतरण विफल रहता है कि यह एक अपवाद बढ़ा नहीं है के साथ संगत संदर्भ प्रकार के बीच एक स्पष्ट कलाकारों के रूप में एक ही काम करता है। बल्कि, यह लक्ष्य चर में एक अशक्त मान पैदा करता है। चूंकि प्रदर्शन के मामले में अपवाद बहुत महंगे हैं, इसलिए इसे कास्टिंग का एक बेहतर तरीका माना जाता है।


यही नहीं, जैसा कि एक कास्टक्लास कहता है और दूसरा आईएल कोड में इस्नास्ट कहता है।
जेनिक्स

0

आप जो चुनते हैं वह दृढ़ता से निर्भर करता है कि क्या आवश्यक है। मैं स्पष्ट कास्टिंग पसंद करता हूं

IMyInterface = (IMyInterface)someobj;

क्योंकि अगर वस्तु IMyInterface प्रकार से होनी चाहिए और यह नहीं है - यह निश्चित रूप से समस्या है। जितनी जल्दी हो सके त्रुटि प्राप्त करना बेहतर है क्योंकि इसके दुष्प्रभाव को ठीक करने के बजाय सटीक त्रुटि को ठीक किया जाएगा।

लेकिन अगर आप उन तरीकों से निपटते हैं जो objectपैरामीटर के रूप में स्वीकार करते हैं तो आपको किसी भी कोड को निष्पादित करने से पहले इसके सटीक प्रकार की जांच करने की आवश्यकता होती है। ऐसे में asयह उपयोगी होगा ताकि आप बच सकें InvalidCastException


0

यह निर्भर करता है, क्या आप "के रूप में" का उपयोग करने के बाद अशक्त के लिए जांचना चाहते हैं या आप अपने एप्लिकेशन को अपवाद फेंकना पसंद करेंगे?

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



0

ओपी की समस्या एक विशिष्ट कास्टिंग स्थिति तक सीमित है। शीर्षक बहुत अधिक स्थितियों को शामिल करता है।
यहां उन सभी प्रासंगिक कास्टिंग स्थितियों का अवलोकन किया गया है जिनके बारे में मैं अभी सोच सकता हूं:

private class CBase
{
}

private class CInherited : CBase
{
}

private enum EnumTest
{
  zero,
  one,
  two
}

private static void Main (string[] args)
{
  //########## classes ##########
  // object creation, implicit cast to object
  object oBase = new CBase ();
  object oInherited = new CInherited ();

  CBase oBase2 = null;
  CInherited oInherited2 = null;
  bool bCanCast = false;

  // explicit cast using "()"
  oBase2 = (CBase)oBase;    // works
  oBase2 = (CBase)oInherited;    // works
  //oInherited2 = (CInherited)oBase;   System.InvalidCastException
  oInherited2 = (CInherited)oInherited;    // works

  // explicit cast using "as"
  oBase2 = oBase as CBase;
  oBase2 = oInherited as CBase;
  oInherited2 = oBase as CInherited;  // returns null, equals C++/CLI "dynamic_cast"
  oInherited2 = oInherited as CInherited;

  // testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
  bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ());    // true
  bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ());    // true
  bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ());    // false
  bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ());    // true

  //########## value types ##########
  int iValue = 2;
  double dValue = 1.1;
  EnumTest enValue = EnumTest.two;

  // implicit cast, explicit cast using "()"
  int iValue2 = iValue;   // no cast
  double dValue2 = iValue;  // implicit conversion
  EnumTest enValue2 = (EnumTest)iValue;  // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')

  iValue2 = (int)dValue;   // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
  dValue2 = dValue;
  enValue2 = (EnumTest)dValue;  // underlying type is int, so "1.1" beomces "1" and then "one"

  iValue2 = (int)enValue;
  dValue2 = (double)enValue;
  enValue2 = enValue;   // no cast

  // explicit cast using "as"
  // iValue2 = iValue as int;   error CS0077: The as operator must be used with a reference type or nullable type
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.