इफ स्टेटमेंट में असाइनमेंट


142

मेरे पास एक वर्ग है Animal, और इसका उपवर्ग है Dog। मैं अक्सर खुद को निम्न पंक्तियों का कोडिंग पाता हूं:

if (animal is Dog)
{
    Dog dog = animal as Dog;    
    dog.Name;    
    ... 
}

चर के लिए Animal animal;

क्या कुछ सिंटैक्स है जो मुझे कुछ लिखने की अनुमति देता है:

if (Dog dog = animal as Dog)
{    
    dog.Name;    
    ... 
}

1
इसका क्या मतलब होगा? boolहालत क्या होगी ?
कर्क वुल्ल

मेरी जानकारी के कोई भी नहीं। नाम तक नहीं ले जाने का कोई कारण?
AlG

22
बस एक नोट, जैसे कोड अक्सर SOLID सिद्धांतों में से एक को तोड़ने का परिणाम हो सकता है । एल - Liskov प्रतिस्थापन सिद्धांत । यह नहीं कहना कि यह गलत है कि आप हर समय क्या कर रहे हैं, लेकिन सोचने लायक हो सकता है।
ckittel

कृपया ध्यान रखें कि @ckittel क्या कर रहा है, आप शायद ऐसा नहीं करना चाहते हैं
khebbie

1
@ सोलो न ; null= falseसी में #; सी # केवल ifशर्तों में वास्तविक बूल या अनुमानित रूप से बूल के लिए परिवर्तनीय चीजों की अनुमति देता है । न तो नल और न ही कोई पूर्णांक प्रकार स्पष्ट रूप से बूल के लिए परिवर्तनीय हैं।
रोमन स्टार्कोव

जवाबों:


323

नीचे दिए गए उत्तर को वर्षों पहले लिखा गया था और समय के साथ अद्यतन किया गया था। C # 7 के अनुसार, आप पैटर्न मिलान का उपयोग कर सकते हैं:

if (animal is Dog dog)
{
    // Use dog here
}

ध्यान दें कि बयान के dogबाद भी गुंजाइश है if, लेकिन निश्चित रूप से असाइन नहीं किया गया है।


नहीं, वहाँ नहीं है। हालांकि इसे लिखना अधिक मुहावरेदार है:

Dog dog = animal as Dog;
if (dog != null)
{
    // Use dog
}

यह देखते हुए कि "जैसा कि यदि बाद में" लगभग हमेशा इस तरह से उपयोग किया जाता है, तो यह एक ऑपरेटर होने के लिए और अधिक समझ में आता है जो एक ही बार में दोनों भागों का प्रदर्शन करता है। यह वर्तमान में C # 6 में नहीं है, लेकिन पैटर्न मिलान प्रस्ताव लागू होने पर C # 7 का हिस्सा हो सकता है।

समस्या यह है कि आप एक कथन 1 के हालत भाग में एक चर घोषित नहीं कर सकते । निकटतम दृष्टिकोण जो मैं सोच सकता हूं वह यह है:if

// EVIL EVIL EVIL. DO NOT USE.
for (Dog dog = animal as Dog; dog != null; dog = null)
{
    ...
}

यह सिर्फ बुरा है ... (मैंने अभी इसकी कोशिश की है, और यह काम करता है। लेकिन कृपया, कृपया ऐसा न करें। ओह, और आप पाठ्यक्रम dogका उपयोग करने varकी घोषणा कर सकते हैं ।)

बेशक आप एक विस्तार विधि लिख सकते हैं:

public static void AsIf<T>(this object value, Action<T> action) where T : class
{
    T t = value as T;
    if (t != null)
    {
        action(t);
    }
}

फिर इसके साथ कॉल करें:

animal.AsIf<Dog>(dog => {
    // Use dog in here
});

वैकल्पिक रूप से, आप दोनों को जोड़ सकते हैं:

public static void AsIf<T>(this object value, Action<T> action) where T : class
{
    // EVIL EVIL EVIL
    for (var t = value as T; t != null; t = null)
    {
        action(t);
    }
}

आप लूप्ड एक्सप्रेशन के बिना एक लूप एक्सप्रेशन को लूप के लिए क्लीनर तरीके से उपयोग कर सकते हैं:

public static IEnumerable<T> AsOrEmpty(this object value)
{
    T t = value as T;
    if (t != null)
    {
        yield return t;
    }
}

फिर:

foreach (Dog dog in animal.AsOrEmpty<Dog>())
{
    // use dog
}

1 आप कथनों में मूल्यों को निर्दिष्ट कर सकते हैंif , हालांकि मैं शायद ही कभी ऐसा करता हूं। हालांकि यह वैरिएबल घोषित करने के समान नहीं है। डेटा की धाराओं को पढ़ते समय यह मेरे लिए बहुत असामान्य नहीं है while। उदाहरण के लिए:

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}

इन दिनों मैं आम तौर पर एक रैपर का उपयोग करना पसंद करता हूं जो मुझे उपयोग करने देता है foreach (string line in ...)लेकिन मैं ऊपर एक सुंदर मुहावरेदार पैटर्न के रूप में देखता हूं। यह आम तौर पर अच्छा एक शर्त के भीतर दुष्प्रभाव के लिए नहीं है, लेकिन विकल्प आमतौर पर कोड दोहराव शामिल है, और जब आप इस पैटर्न पता है कि यह सही पाने के लिए आसान है।


76
+1 जवाब देने और भीख माँगने के लिए कि ओपी इसका उपयोग नहीं करता है। तत्काल क्लासिक।
ckittel

8
@ पाओल: अगर मैं इसे किसी को बेचने की कोशिश कर रहा था , तो मैं दृढ़ता से उन्हें इसका इस्तेमाल न करने की सलाह दूंगा। मैं वही दिखा रहा हूं जो संभव है
जॉन स्कीट

12
@Paul: मुझे लगता है कि इसके पीछे प्रेरणा हो सकती है EVIL EVIL EVIL, लेकिन मैं सकारात्मक नहीं हूं।
एडम रॉबिन्सन

18
मैंने थोड़ी देर पहले एक समान विस्तार विधि (ओवरलोड का एक गुच्छा के साथ) बनाई थी और मैंने उन्हें बुलाया AsEither(...), मुझे लगता है कि यह थोड़ा स्पष्ट है AsIf(...), इसलिए मैं लिख सकता हूं myAnimal.AsEither(dog => dog.Woof(), cat => cat.Meeow(), unicorn => unicorn.ShitRainbows())
हर्जमेस्टर

97
यह सी # का सबसे अच्छा दुरुपयोग है मैंने थोड़ी देर में देखा है। स्पष्ट रूप से आप एक दुष्ट प्रतिभा हैं।
एरिक लिपर्ट

48

यदि asविफल रहता है, तो यह वापस आ जाता है null

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}

सबसे पहले, धन्यवाद। दूसरा, मैं ifबयान के दायरे में डॉग वैरिएबल बनाना चाहता हूं न कि बाहरी दायरे में।
माइकल

@ मिचेल आप ऐसा नहीं कर सकते यदि एक बयान में। अगर बूल का परिणाम होना चाहिए तो असाइनमेंट नहीं। जॉन स्कीट आपको कुछ अच्छे जेनेरिक और लैम्ब्डा संयोजन प्रदान करता है जिन पर आप विचार कर सकते हैं।
रॉडने एस। फोली

ifएक बूल परिणाम और एक काम हो सकता है। Dog dog; if ((dog = animal as Dog) != null) { // Use Dog }लेकिन वह अभी भी बाहरी दायरे में चर का परिचय देता है।
टॉम मेफील्ड

12

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

public void Test()
{
    var animals = new Animal[] { new Dog(), new Duck() };

    foreach (var animal in animals)
    {
        {   // <-- scopes the existence of critter to this block
            Dog critter;
            if (null != (critter = animal as Dog))
            {
                critter.Name = "Scopey";
                // ...
            }
        }

        {
            Duck critter;
            if (null != (critter = animal as Duck))
            {
                critter.Fly();
                // ...
            }
        }
    }
}

यह सोचते हैं

public class Animal
{
}

public class Dog : Animal
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            Console.WriteLine("Name is now " + _name);
        }
    }
}

public class Duck : Animal
{
    public void Fly()
    {
        Console.WriteLine("Flying");
    }
}

उत्पादन हो जाता है:

Name is now Scopey
Flying

परीक्षण में चर असाइनमेंट के पैटर्न का उपयोग धाराओं से बाइट ब्लॉक पढ़ते समय किया जाता है, उदाहरण के लिए:

int bytesRead = 0;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) 
{
    // ...
}

हालांकि, ऊपर इस्तेमाल किया गया वैरिएबल स्कोपिंग का पैटर्न, विशेष रूप से सामान्य कोड पैटर्न नहीं है और अगर मैंने देखा कि इसका उपयोग पूरे स्थान पर किया जा रहा है तो मैं इसे रिफैक्ट करने का तरीका खोज रहा हूँ।


11

क्या कुछ सिंटैक्स है जो मुझे कुछ लिखने की अनुमति देता है:

if (Dog dog = animal as Dog) { ... dog ... }

?

संभावना सी # 6.0 में होगी। इस सुविधा को "घोषणा अभिव्यक्ति" कहा जाता है। देख

https://roslyn.codeplex.com/discussions/565640

ब्योरा हेतु।

प्रस्तावित वाक्यविन्यास है:

if ((var i = o as int?) != null) {  i  }
else if ((var s = o as string) != null) {  s  }
else if ...

आम तौर पर, प्रस्तावित विशेषता यह है कि एक स्थानीय चर घोषणा का उपयोग अभिव्यक्ति के रूप में किया जा सकता है । यह ifसिंटैक्स अधिक सामान्य विशेषता का सिर्फ एक अच्छा परिणाम है।


1
एक नज़र में यह कम पठनीय लगता है तो बस आज आप जैसा चर घोषित कर रहे हैं। क्या आप जानते हैं कि यह विशेष सुविधा -100 पॉइंट बार पास करने में क्यों कामयाब रही है?
asawyer

3
@ समीर: सबसे पहले, यह एक बहुत बार अनुरोधित सुविधा है। दूसरा, अन्य भाषाओं में "यदि" का यह विस्तार है; उदाहरण के लिए gcc C ++ में समकक्ष की अनुमति देता है। तीसरा, जैसा कि मैंने उल्लेख किया है कि यह फीचर "अगर" से अधिक सामान्य है। चौथा, अधिक से अधिक चीजें बनाने के लिए C # 3.0 के बाद से C # में एक प्रवृत्ति है जिसके लिए एक बयान संदर्भ की आवश्यकता होती है, एक अभिव्यक्ति संदर्भ की आवश्यकता होती है; यह कार्यात्मक-शैली प्रोग्रामिंग के साथ मदद करता है। अधिक विवरण के लिए भाषा डिज़ाइन नोट देखें।
एरिक लिपर्ट

2
@ समीर: आपका स्वागत है! यदि आपके पास अधिक टिप्पणियाँ हैं, तो Roslyn.codeplex.com पर चर्चा में भाग लेने के लिए स्वतंत्र महसूस करें। इसके अलावा, मैं जोड़ूंगा: पांचवां, नया रोसलिन बुनियादी ढांचा इन प्रकार की छोटी प्रयोगात्मक सुविधाओं को करने की कार्यान्वयन टीम के लिए मामूली लागत को कम करता है, जिसका अर्थ है कि "माइनस 100" अंक का परिमाण कम हो जाता है। टीम इस अवसर को पूरी तरह से सभ्य छोटी विशेषताओं का पता लगाने के लिए ले रही है जो लंबे समय से अनुरोध किए गए हैं, लेकिन इसे पहले -100 अंक बाधा के ऊपर कभी नहीं बनाया गया।
एरिक लिपर्ट सेप

1
इन टिप्पणियों के पाठक जो "अंक" के बारे में उलझन में हैं, हम इस विषय पर पूर्व सी # डिजाइनर एरिक गुनर्सन के ब्लॉग पोस्ट को पढ़ना चाहिए: blogs.msdn.com/b/ericgu/archive/2004/01/12/57985 aspx । यह एक सादृश्य है; कोई वास्तविक "अंक" नहीं गिना जा रहा है।
एरिक लिपर्ट सेप

@ समीर: मुझे लगता है कि यह सुविधा वास्तव में कॉल करने के लिए चमकती है Try*(जैसे, TryParse)। यह सुविधा न केवल इस तरह के कॉल को एक एकल अभिव्यक्ति (जैसा कि उन्हें, आईएमओ होना चाहिए) में करती है, बल्कि इस तरह के चर के क्लीनर स्कूपिंग के लिए भी अनुमति देती है। मैं outइस Tryपद्धति के पैरामीटर को इसके सशर्त करने के लिए तैयार करने के बारे में उत्साहित हूं ; इससे कुछ प्रकार के बगों को पेश करना कठिन हो जाता है।
ब्रायन

9

विस्तार विधियों में से एक मैं अपने आप को लिखने और अक्सर * का उपयोग कर पाता हूं

public static TResult IfNotNull<T,TResult>(this T obj, Func<T,TResult> func)
{
    if(obj != null)
    {
        return func(obj);
    }
    return default(TResult);
}

जिसका उपयोग इस स्थिति में किया जा सकता है

string name = (animal as Dog).IfNotNull(x => x.Name);

और फिर nameकुत्ते का नाम है (यदि यह एक कुत्ता है), अन्यथा अशक्त।

* मुझे नहीं पता कि यह प्रदर्शन है। यह प्रोफाइलिंग में कभी अड़चन के रूप में नहीं आया है।


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

आप डिफॉल्टवैल्यू को एक तर्क के रूप में क्यों लेंगे और कॉल करने वाले को यह तय करने दें कि मैंने डिफॉल्ट (....) पर वापस गिरने के बजाय इसे क्या कहा है?
त्रिशूल

5

यहाँ अनाज के खिलाफ जा रहे हैं, लेकिन शायद आप इसे पहली जगह में गलत कर रहे हैं। एक वस्तु के प्रकार के लिए जाँच लगभग हमेशा एक कोड गंध है। आपके उदाहरण में, सभी जानवरों का नाम नहीं है? फिर सिर्फ Animal.name को कॉल करें, बिना यह जांचे कि यह कुत्ता है या नहीं।

वैकल्पिक रूप से, इस विधि को उल्टा कर दें ताकि आप एनिमल पर एक ऐसी विधि कहें जो कुछ अलग तरीके से पशु के ठोस प्रकार पर निर्भर करती है। इन्हें भी देखें: बहुरूपता



3

यहाँ कुछ अतिरिक्त गंदा कोड (जॉन के रूप में गंदा नहीं है, हालांकि :-)) आधार वर्ग को संशोधित करने पर निर्भर है। मुझे लगता है कि यह शायद इस बिंदु को याद करते हुए इरादे को पकड़ लेता है:

class Animal
{
    public Animal() { Name = "animal";  }
    public List<Animal> IfIs<T>()
    {
        if(this is T)
            return new List<Animal>{this};
        else
            return new List<Animal>();
    }
    public string Name;
}

class Dog : Animal
{
    public Dog() { Name = "dog";  }
    public string Bark { get { return "ruff"; } }
}


class Program
{
    static void Main(string[] args)
    {
        var animal = new Animal();

        foreach(Dog dog in animal.IfIs<Dog>())
        {
            Console.WriteLine(dog.Name);
            Console.WriteLine(dog.Bark);
        }
        Console.ReadLine();
    }
}

3

यदि आपको एक के बाद एक कई-जैसे करना है, (और बहुरूपता का उपयोग करना एक विकल्प नहीं है), तो एक स्विचऑन टाइप निर्माण का उपयोग करने पर विचार करें ।


3

समस्या (सिंटैक्स के साथ) असाइनमेंट के साथ नहीं है , क्योंकि सी # में असाइनमेंट ऑपरेटर एक वैध अभिव्यक्ति है। बल्कि, यह वांछित घोषणा के साथ है क्योंकि घोषणाएं बयान हैं।

अगर मुझे इस तरह कोड लिखना होगा तो मैं कभी-कभी (बड़े संदर्भ के आधार पर) कोड को इस तरह लिखूंगा:

Dog dog;
if ((dog = animal as Dog) != null) {
    // use dog
}

उपरोक्त सिंटैक्स के साथ गुण हैं (जो अनुरोध किए गए सिंटैक्स के करीब है) क्योंकि:

  1. का उपयोग करते हुए dog बाहरif एक संकलन त्रुटि हो के रूप में यह एक मूल्य कहीं निर्दिष्ट नहीं की गई होगी। (अर्थात, कहीं और असाइन करें dog।)
  2. इस दृष्टिकोण को भी अच्छी तरह से विस्तारित किया जा सकता है if/else if/...( asएक उपयुक्त शाखा का चयन करने के लिए केवल उतने ही आवश्यक हैं; यह बड़ा मामला जहां मैं इसे इस रूप में लिखता हूं जब मुझे चाहिए।)
  3. के दोहराव से बचा जाता है is/as। (लेकिन Dog dog = ...फार्म के साथ भी किया ।)
  4. "मुहावरेदार जबकि" से अलग नहीं है। (बस दूर नहीं किया जाता है: सशर्त एक सुसंगत रूप में और सरल रखें।)

dogदुनिया के बाकी हिस्सों से वास्तव में अलग करने के लिए एक नए ब्लॉक का उपयोग किया जा सकता है:

{
  Dog dog = ...; // or assign in `if` as per above
}
Bite(dog); // oops! can't access dog from above

खुश कोडिंग।


बिंदु # 1 जो आप प्रदान करते हैं वह पहली चीज है जो मेरे दिमाग में आई थी। वैरिएबल को डिक्लेयर करें लेकिन केवल इफ में असाइन करें। चर के बाद बाहर से नहीं देखा जा सकता है अगर एक संकलक त्रुटि के बिना - एकदम सही!
इयान येट्स

1

आप ऐसा कुछ उपयोग कर सकते हैं

// घोषित चर बूल टेम्प = झूठी;

 if (previousRows.Count > 0 || (temp= GetAnyThing()))
                                    {
                                    }

0

विस्तार विधियों के साथ एक और EVIL समाधान :)

public class Tester
{
    public static void Test()
    {
        Animal a = new Animal();

        //nothing is printed
        foreach (Dog d in a.Each<Dog>())
        {
            Console.WriteLine(d.Name);
        }

        Dog dd = new Dog();

        //dog ID is printed
        foreach (Dog dog in dd.Each<Dog>())
        {
            Console.WriteLine(dog.ID);
        }
    }
}

public class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal constructued:" + this.ID);
    }

    private string _id { get; set; }

    public string ID { get { return _id ?? (_id = Guid.NewGuid().ToString());} }

    public bool IsAlive { get; set; }
}

public class Dog : Animal 
{
    public Dog() : base() { }

    public string Name { get; set; }
}

public static class ObjectExtensions
{
    public static IEnumerable<T> Each<T>(this object Source)
        where T : class
    {
        T t = Source as T;

        if (t == null)
            yield break;

        yield return t;
    }
}

मैं व्यक्तिगत रूप से स्वच्छ तरीका पसंद करता हूं:

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}

0

अगर एक बयान यह अनुमति नहीं देगा, लेकिन एक लूप के लिए होगा।

जैसे

for (Dog dog = animal as Dog; dog != null; dog = null)
{
    dog.Name;    
    ... 
}

यदि यह काम करता है तो तुरंत स्पष्ट नहीं होता है, तो इस प्रक्रिया का चरणबद्ध तरीके से पालन करना है:

  • वैरिएबल डॉग को टाइप डॉग के रूप में बनाया जाता है और वैरिएबल एनिमल को असाइन किया जाता है जिसे डॉग के लिए रखा जाता है।
  • यदि असाइनमेंट विफल हो जाता है, तो कुत्ता अशक्त है, जो लूप की सामग्री को चलने से रोकता है, क्योंकि यह तुरंत टूट गया है।
  • यदि असाइनमेंट सफल होता है तो लूप फॉरएरेशन से चलता है
  • पुनरावृत्ति के अंत में, कुत्ते के चर को नल का एक मान दिया जाता है, जो लूप के लिए टूट जाता है।


0

IDK अगर यह किसी को भी मदद करता है, लेकिन आप हमेशा अपने चर को असाइन करने के लिए एक TryParse का उपयोग करने की कोशिश कर सकते हैं। यहाँ एक उदाहरण है:

if (int.TryParse(Add(Value1, Value2).ToString(), out total))
        {
            Console.WriteLine("I was able to parse your value to: " + total);
        } else
        {
            Console.WriteLine("Couldn't Parse Value");
        }


        Console.ReadLine();
    }

    static int Add(int value1, int value2)
    {
        return value1 + value2;
    }

कुल चर अपने अगर बयान से पहले घोषित की जाएगी।


0

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

var dog = animal as Dog; if (dog != null)
{
    Console.WriteLine("Parent Dog Name = " + dog.name);

    var purebred = dog.Puppy as Purebred; if (purebred != null)
    {
         Console.WriteLine("Purebred Puppy Name = " + purebred.Name);
    }

    var mutt = dog.Puppy as Mongrel; if (mutt != null)
    {
         Console.WriteLine("Mongrel Puppy Name = " + mutt.Name);
    }
 }

0

मुझे पता है कि मैं पार्टी के लिए सुपर डुपर हूं, लेकिन मुझे लगा कि मैं इस दुविधा के लिए अपना वर्कअराउंड पोस्ट करूंगा क्योंकि मैंने इसे अभी तक यहां (या उस मामले के लिए कहीं भी) नहीं देखा है।

/// <summary>
/// IAble exists solely to give ALL other Interfaces that inherit IAble the TryAs() extension method
/// </summary>
public interface IAble { }

public static class IAbleExtension
{
    /// <summary>
    /// Attempt to cast as T returning true and out-ing the cast if successful, otherwise returning false and out-ing null
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="able"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public static bool TryAs<T>(this IAble able, out T result) where T : class
    {
        if (able is T)
        {
            result = able as T;
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }

    /// <summary>
    /// Attempt to cast as T returning true and out-ing the cast if successful, otherwise returning false and out-ing null
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public static bool TryAs<T>(this UnityEngine.Object obj, out T result) where T : class
    {
        if (obj is T)
        {
            result = obj as T;
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }
}

इसके साथ, आप इस तरह के काम कर सकते हैं:

if (animal.TryAs(out Dog dog))
{
    //Do Dog stuff here because animal is a Dog
}
else
{
    //Cast failed! animal is not a dog
}

महत्वपूर्ण नोट: यदि आप एक इंटरफ़ेस का उपयोग करके TryAs () का उपयोग करना चाहते हैं, तो आपको IAble को प्राप्त करना होगा।

का आनंद लें! 🙂

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