2009-08-18 22 views

Trả lời

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

Nhưng thực tế, nó có mùi.

Về cú pháp lambda:

x => x.something 

là loại một phím tắt và có thể được viết như

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

Gọi một chiếu trong đó có mà cố gắng/catch:

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

... 

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

Phải thừa nhận rằng tôi muốn hiếm khi muốn sử dụng kỹ thuật này. Nó giống như một sự lạm dụng ngoại lệ nói chung, nhưng đôi khi có các API khiến bạn không còn lựa chọn nào khác.

(Tôi đã gần như chắc chắn đặt nó trong một phương pháp riêng biệt chứ không phải là đưa nó "inline" như một biểu thức lambda mặc dù.)

+0

Bạn cho rằng lạm dụng ngoại lệ là gì? Trong logic kinh doanh của tôi, tôi có một phương pháp để tính lương của nhân viên. Có nhiều thứ khác nhau có thể sai lầm như người lao động không có lương. Tôi đã thực hiện một ngoại lệ tùy chỉnh tôi có thể ném và bắt trong bộ điều khiển của tôi và đưa vào mô hình xem của tôi. Thật tồi tệ? – Pluc

+0

@Pluc: Nhân viên * có nghĩa là * để có một bộ lương không? Nếu vậy, nó thiếu âm thanh đặc biệt. Nếu nó có phần dự kiến, tôi có lẽ sẽ không sử dụng ngoại lệ để xử lý nó. –

2

Khi giao dịch với LINQ bạn sẽ thường thấy kịch bản mà biểu hiện của bạn có thể tạo ra không mong muốn tác dụng phụ. Như Jon đã nói, cách tốt nhất để chống lại những loại vấn đề này là có các phương thức tiện ích mà biểu thức LINQ của bạn có thể sử dụng để xử lý những điều này một cách duyên dáng và theo cách mà sẽ không làm nổ tung mã của bạn. Ví dụ, tôi có một phương pháp tôi đã phải sử dụng thời gian mà kết thúc tốt đẹp một TryParse để cho tôi biết nếu một cái gì đó là một số. Có rất nhiều ví dụ khác tất nhiên.

Một trong những hạn chế của cú pháp biểu thức là có rất nhiều thứ mà nó không thể thực hiện một cách duyên dáng hoặc thậm chí là hoàn toàn không thực hiện tạm thời biểu thức để xử lý một kịch bản nhất định. Phân tích cú pháp một tập con của các mục trong một tệp XML là một ví dụ tuyệt vời. Hãy thử phân tích một bộ sưu tập cha mẹ phức tạp với các tập con con từ một tệp XML trong một biểu thức duy nhất và bạn sẽ sớm thấy mình viết một vài phần biểu thức mà tất cả cùng nhau tạo thành toàn bộ hoạt động.

4

Một biến thể của giải pháp Stefan cho cú pháp hiểu:

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

Mặc dù, nó là "có mùi" quá, nhưng vẫn tiếp cận này đôi khi có thể được sử dụng để chạy mã với tác dụng phụ bên trong biểu hiện.

3

Trong trường hợp bạn cần Biểu thức thay vì hàm lambda (ví dụ:khi lựa chọn từ IQueryable), bạn có thể sử dụng một cái gì đó như thế này:

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; 
    } 
} 

Cách sử dụng:

[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

Tôi đã đi kèm với một phần mở rộng nhỏ khi tôi nhanh chóng muốn thử/bắt mỗi lần lặp của một IEnumerable<T>

Cách sử dụng

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(); 
} 

Việc gia hạn

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; } 
    } 
} 

Chúng tôi cuối cùng có thể đi một chút nữa bởi có một phần mở rộng SkipOnException, chấp nhận tùy chọn một trình xử lý ngoại lệ ví dụ.

Các vấn đề liên quan