2009-08-18 17 views
32

उदाहरण:क्या LINQ प्रश्नों के भीतर अपवादों को संभालना संभव है?

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)); 

कैसे यह काम भले ही यह अपवाद फेंकता बनाने के लिए? एक डिफ़ॉल्ट मान मामले के साथ एक कोशिश पकड़ ब्लॉक की तरह एक अपवाद फेंक दिया जाता है ...

उत्तर

34
myEnumerable.Select(a => 
    { 
    try 
    { 
     return ThisMethodMayThrowExceptions(a)); 
    } 
    catch(Exception) 
    { 
     return defaultValue; 
    } 
    }); 

लेकिन वास्तव में, इसमें कुछ गंध है।

बारे में लैम्ब्डा वाक्य रचना:

x => x.something 

एक शॉर्टकट की तरह है और के रूप में

(x) => { return x.something; } 
18

कॉल एक प्रक्षेपण जो कि कोशिश/पकड़ है लिखा जा सकता है:

myEnumerable.Select(a => TryThisMethod(a)); 

... 

public static Bar TryThisMethod(Foo a) 
{ 
    try 
    { 
     return ThisMethodMayThrowExceptions(a); 
    } 
    catch(BarNotFoundException) 
    { 
     return Bar.Default; 
    } 
} 

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

(मैं लगभग निश्चित रूप से यह एक अलग विधि में बल्कि यह "इनलाइन" एक लैम्ब्डा अभिव्यक्ति के रूप में यद्यपि डालने से डाल था।)

+0

आप अपवाद के दुरुपयोग पर क्या विचार करते हैं? मेरे व्यवसाय तर्क में, मेरे पास कर्मचारी के वेतन की गणना करने का एक तरीका है। ऐसी कई चीजें हैं जो कर्मचारी की तरह गलत हो सकती हैं, जिसमें वेतन सेट नहीं है। मैंने एक कस्टम अपवाद बनाया है जिसे मैं अपने नियंत्रक में फेंक और पकड़ सकता हूं और अपने दृश्य मॉडल में डाल सकता हूं। यह बुरी बात है? – Pluc

+0

@Pluc: क्या कर्मचारी * मतलब * वेतन सेट है? यदि ऐसा है, तो यह असाधारण लगता है। अगर यह कुछ हद तक अपेक्षित है, तो शायद मैं इसे संभालने के लिए अपवादों का उपयोग नहीं करता। –

2

जब LINQ के साथ काम कर आप आमतौर पर परिदृश्य मिल जाएगा जहाँ आपके अभिव्यक्ति अवांछित उत्पादन कर सकता है दुष्प्रभाव। जैसा कि जॉन ने कहा था, इस तरह की समस्याओं का मुकाबला करने का सबसे अच्छा तरीका यह है कि यूटिलिटी विधियों का उपयोग करने के लिए आपकी LINQ अभिव्यक्ति का उपयोग किया जा सकता है जो इन्हें खूबसूरती से और एक फैशन में संभाल देगा जो आपके कोड को उड़ाएगा। उदाहरण के लिए, मेरे पास एक विधि है जिसे मुझे समय-समय पर उपयोग करना पड़ता है जो ट्राईपर्स को लपेटता है ताकि मुझे यह बताने के लिए कि कुछ संख्या है या नहीं। पाठ्यक्रम के कई अन्य उदाहरण हैं।

अभिव्यक्ति वाक्यविन्यास की सीमाओं में से एक यह है कि ऐसी कई चीजें हैं जो किसी भी परिदृश्य को अस्थायी रूप से अभिव्यक्त करने के लिए अस्थायी रूप से अभिव्यक्ति से निष्पादन को तोड़ने के बिना या तो पूरी तरह से या यहां तक ​​कि पूरी तरह से नहीं कर सकती हैं। एक एक्सएमएल फ़ाइल में वस्तुओं का एक सबसेट पार्स करना अद्भुत उदाहरण है। एक एक्सएमएल फ़ाइल से एक एकल अभिव्यक्ति के भीतर एक बच्चे के सबसेट के साथ एक जटिल अभिभावक संग्रह को पार्स करने का प्रयास करें और आप जल्द ही अपने आप को कई अभिव्यक्ति टुकड़े लिखेंगे जो सभी एक साथ पूरे ऑपरेशन को बनाने के लिए आते हैं।

4

समझ वाक्य रचना के लिए स्टीफन के समाधान के एक बदलाव:

from a in myEnumerable 
select (new Func<myType>(() => { 
    try 
    { 
     return ThisMethodMayThrowExceptions(a)); 
    } 
    catch(Exception) 
    { 
     return defaultValue; 
    } 
}))(); 

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

3

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

public static class ExpressionHelper 
{ 
    public static Expression<Func<TSource, TResult>> TryDefaultExpression<TSource, TResult>(Expression<Func<TSource, TResult>> success, TResult defaultValue) 
    { 
     var body = Expression.TryCatch(success.Body, Expression.Catch(Expression.Parameter(typeof(Exception)), Expression.Constant(defaultValue, typeof (TResult)))); 
     var lambda = Expression.Lambda<Func<TSource, TResult>>(body, success.Parameters); 

     return lambda; 
    } 
} 

उपयोग:

[Test] 
public void Test() 
{ 
    var strings = new object [] {"1", "2", "woot", "3", Guid.NewGuid()}.AsQueryable(); 
    var ints = strings.Select(ExpressionHelper.TryDefaultExpression<object, int>(x => Convert.ToInt32(x), 0)); 
    Assert.IsTrue(ints.SequenceEqual(new[] {1, 2, 0, 3, 0})); 
} 
1

मैं एक छोटे से विस्तार के साथ आ गए हैं जब मैं जल्दी से कोशिश करने के लिए/एक IEnumerable<T>

के हर यात्रा को पकड़ने के लिए चाहते हैं

प्रयोग

public void Test() 
{ 
    List<string> completedProcesses = initialEnumerable 
     .SelectTry(x => RiskyOperation(x)) 
     .OnCaughtException(exception => { _logger.Error(exception); return null; }) 
     .Where(x => x != null) // filter the ones which failed 
     .ToList(); 
} 

विस्तार

public static class OnCaughtExceptionExtension 
{ 
    public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) 
    { 
     foreach (TSource element in enumerable) 
     { 
      SelectTryResult<TSource, TResult> returnedValue; 
      try 
      { 
       returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); 
      } 
      catch (Exception ex) 
      { 
       returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); 
      } 
      yield return returnedValue; 
     } 
    } 

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) 
    { 
     return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); 
    } 

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) 
    { 
     return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); 
    } 

    public class SelectTryResult<TSource,TResult> 
    { 
     internal SelectTryResult(TSource source, TResult result, Exception exception) 
     { 
      Source = source; 
      Result = result; 
      CaughtException = exception; 
     } 

     public TSource Source { get; private set; } 
     public TResult Result { get; private set; } 
     public Exception CaughtException { get; private set; } 
    } 
} 

हम अंततः एक SkipOnException विस्तार होने, वैकल्पिक रूप से, उदाहरण के लिए अपवाद संचालक को स्वीकार कर आगे एक सा जा सकते हैं।

संबंधित मुद्दे