'' null check के साथ कास्ट कास्ट है


107

मैंने देखा कि रेस्परर सुझाव देता है कि मैं इसे चालू करता हूं:

if (myObj.myProp is MyType)
{
   ...
}

इस मामले में:

var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
   ...
}

यह इस बदलाव का सुझाव क्यों देगा? मैं अनुकूलन परिवर्तनों और कोड में कमी के परिवर्तनों का सुझाव देने के लिए रेस्परर का उपयोग कर रहा हूं, लेकिन ऐसा लगता है कि यह मेरा एकल कथन लेना चाहता है और इसे दो-लाइनर में बदलना है।

MSDN के अनुसार :

एक है अभिव्यक्ति सही का आकलन करता है, तो निम्न स्थितियों के दोनों मिले हैं:

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

क्या मैं ऐसा गलत प्रचार कर रहा हूँ, या isठीक उसी जाँच को नहीं करता है, बस एक लाइन में बिना स्पष्ट जाँच के दूसरा स्थानीय चर बनाने की आवश्यकता है?


1
क्या आप कोड में बाद में myObjRef का उपयोग कर रहे हैं? यदि आप हैं, तो आपको MyPropइस परिवर्तन के बाद प्राप्त करने वाले की आवश्यकता नहीं होगी ।
डिफ़ॉल्ट

जवाबों:


146

क्योंकि केवल एक ही कास्ट है। इसकी तुलना करें:

if (myObj.myProp is MyType) // cast #1
{
    var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time
                                         // before using it as a MyType
    ...
}

इसके लिए:

var myObjRef = myObj.myProp as MyType; // only one cast
if (myObjRef != null)
{
    // myObjRef is already MyType and doesn't need to be cast again
    ...
}

पैटर्न मिलान का उपयोग करके C # 7.0 अधिक कॉम्पैक्ट सिंटैक्स का समर्थन करता है :

if (myObj.myProp is MyType myObjRef)
{
    ...
}

3
बिल्कुल सही। '' का उपयोग करना मूल रूप से रिटर्न ((myPype के रूप में myProp) == नल) जैसा कुछ कर रहा है
बंबू

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

4
यह भी ध्यान दें कि मूल संस्करण थ्रेड-सुरक्षित नहीं है। myObjया कास्ट और कास्ट के myPropबीच का मूल्य (दूसरे धागे द्वारा) बदला जा सकता है is, जिससे अवांछनीय व्यवहार हो सकता है।
जेफ ई

1
मैं भी जोड़ सकते हैं कि का उपयोग कर as+ != nullभी ओवरराइड निष्पादित करेंगे !=के ऑपरेटर MyTypeअगर परिभाषित (भले ही myObjRefशून्य है)। जबकि ज्यादातर मामलों में यह एक गैर-मुद्दा है (खासकर यदि आप इसे ठीक से लागू करते हैं), कुछ चरम मामलों में (खराब कोड, प्रदर्शन) यह वांछित नहीं हो सकता है। ( हालांकि बहुत चरम होगा )
क्रिस सिनक्लेयर

1
@ क्रिस: सही, कोड का सही अनुवाद का उपयोग करेगा object.ReferenceEquals(null, myObjRef)
बेन वोइगट

10

सबसे अच्छा विकल्प इस तरह से मिलान पैटर्न का उपयोग है:

if (value is MyType casted){
    //Code with casted as MyType
    //value is still the same
}
//Note: casted can be used outside (after) the 'if' scope, too

यह सवाल से दूसरे टुकड़े से कितना बेहतर है?
विक्टर यारमा

प्रश्न का दूसरा टुकड़ा मूल उपयोग (चर घोषणा के बिना) के संदर्भ में है और उस स्थिति में आप दो बार टाइप करेंगे (एक बयान में और दूसरा कास्ट से पहले)
फ्रांसेस्को कैटोनी

6

बेल्ट के नीचे वास्तव में क्या होता है, इसके बारे में अभी तक कोई जानकारी नहीं है। इस उदाहरण पर एक नज़र डालें:

object o = "test";
if (o is string)
{
    var x = (string) o;
}

यह निम्नलिखित IL का अनुवाद करता है:

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  ldnull      
IL_000E:  cgt.un      
IL_0010:  stloc.1     
IL_0011:  ldloc.1     
IL_0012:  brfalse.s   IL_001D
IL_0014:  nop         
IL_0015:  ldloc.0     // o
IL_0016:  castclass   System.String
IL_001B:  stloc.2     // x
IL_001C:  nop         
IL_001D:  ret   

यहाँ क्या मायने रखता है isinstऔर castclassकॉल - दोनों अपेक्षाकृत महंगे हैं। यदि आप विकल्प से तुलना करते हैं तो आप देख सकते हैं कि यह केवल एक isinstचेक करता है :

object o = "test";
var oAsString = o as string;
if (oAsString != null)
{

}

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  stloc.1     // oAsString
IL_000E:  ldloc.1     // oAsString
IL_000F:  ldnull      
IL_0010:  cgt.un      
IL_0012:  stloc.2     
IL_0013:  ldloc.2     
IL_0014:  brfalse.s   IL_0018
IL_0016:  nop         
IL_0017:  nop         
IL_0018:  ret  

यह भी ध्यान देने योग्य है कि मूल्य प्रकार इसके unbox.anyबजाय उपयोग करेगा castclass:

object o = 5;
if (o is int)
{
    var x = (int)o;
}

IL_0000:  nop         
IL_0001:  ldc.i4.5    
IL_0002:  box         System.Int32
IL_0007:  stloc.0     // o
IL_0008:  ldloc.0     // o
IL_0009:  isinst      System.Int32
IL_000E:  ldnull      
IL_000F:  cgt.un      
IL_0011:  stloc.1     
IL_0012:  ldloc.1     
IL_0013:  brfalse.s   IL_001E
IL_0015:  nop         
IL_0016:  ldloc.0     // o
IL_0017:  unbox.any   System.Int32
IL_001C:  stloc.2     // x
IL_001D:  nop         
IL_001E:  ret   

ध्यान दें कि यह आवश्यक रूप से तेज़ परिणाम में अनुवाद नहीं करता है जैसा कि हम यहाँ देख सकते हैं । ऐसा प्रतीत होता है कि उस प्रश्न के बाद से सुधार किए गए थे, हालांकि लगता है कि कलाकारों का प्रदर्शन उतनी ही तेजी से किया जाता था, जितना पहले हुआ करते थे asऔर linqअब लगभग 3 गुना तेजी से हो रहे हैं।


4

रिचार्पर चेतावनी:

"Type check and direct cast can be replaced with try cast and check for null"

दोनों काम करेंगे, यह निर्भर करता है कि आपका कोड आपको कैसे अधिक सूट करता है। मेरे मामले में मैं सिर्फ उस चेतावनी को नजरअंदाज करता हूं:

//1st way is n+1 times of casting
if (x is A) ((A)x).Run();
else if (x is B) ((B)x).Run();
else if (x is C) ((C)x).Run();
else if (x is D) ((D)x).Run();
//...
else if (x is N) ((N)x).Run();    
//...
else if (x is Z) ((Z)x).Run();

//2nd way is z times of casting
var a = x as Type A;
var b = x as Type B;
var c = x as Type C;
//..
var n = x as Type N;
//..
var z = x as Type Z;
if (a != null) a.Run();
elseif (b != null) b.Run();
elseif (c != null) c.Run();
...
elseif (n != null) n.Run();
...
elseif (x != null) x.Run();

मेरे कोड में दूसरा तरीका लंबा और खराब प्रदर्शन है।


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

3

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

प्रकार की लागत की तुलना में एक स्थानीय चर बनाने की लागत बहुत नगण्य है।

पठनीयता और गुंजाइश मेरे लिए आम तौर पर अधिक महत्वपूर्ण कारक हैं। मैं ReSharper से असहमत हूं, और अकेले उस कारण के लिए "is" ऑपरेटर का उपयोग कर रहा हूं; बाद में अनुकूलित करें अगर यह एक सच्ची अड़चन है।

(मैं मान रहा हूँ कि आप केवल myObj.myProp is MyTypeइस फ़ंक्शन में एक बार उपयोग कर रहे हैं )


0

यह एक दूसरे बदलाव का सुझाव देना चाहिए:

(MyType)myObj.myProp

में

myObjRef

यह मूल कोड की तुलना में एक प्रॉपर्टी एक्सेस और एक कास्ट बचाता है। लेकिन इसे बदलने के बाद ही संभव isहै as


@Default: नहीं, यह नहीं है। इसका मतलब यह नहीं है कि यह कोड में नहीं है।
बेन वोइग्ट

1
क्षमा करें .. गलत समझा गया। हालांकि, (MyType)अगर अपवाद विफल रहता है तो अपवाद को फेंक देंगे। asकेवल लौटता है null
डिफॉल्ट

@ डफॉल्ट: कास्ट विफल नहीं होगा, क्योंकि टाइप पहले ही चेक किया जा चुका है is(यह कोड प्रश्न में है)।
बेन वायगट

1
हालाँकि, re # उस कोड को बदलना चाहता है - मतलब यह कि सुझाए गए बदलाव के बाद ऐसा नहीं होगा।
डिफ़ॉल्ट

मुझे लगता है कि मैं यहां आपके विचार का अनुसरण कर रहा हूं (बस मुझे कुछ समय लगा)। तुम्हारा मतलब है कि पहली पंक्ति है कोड में कहीं और है कि रेखा दूसरी पंक्ति को पुन # सुझाव के बाद सरलीकृत किया जाएगा?
डिफॉल्ट

0

मैं कहूंगा कि यह myObj.myProp का एक जोरदार-टाइप संस्करण है, जो कि myObjRef है। यह तब उपयोग किया जाना चाहिए जब आप ब्लॉक में इस मान को संदर्भित कर रहे हैं, बनाम एक कास्ट करने के लिए।

उदाहरण के लिए, यह:

myObjRef.SomeProperty

इससे बेहतर है:

((MyType)myObj.myProp).SomeProperty
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.