2012-01-19 38 views
12

Tôi đang cố gắng cải thiện kỹ năng của mình bằng cách sử dụng các khối Thử Catch và xử lý lỗi tốt hơn.Trả lại nhiều kết quả từ một phương thức

Tôi có một lớp thực hiện tác vụ chung, trong trường hợp này truy xuất một Facebook AccessToken. Nếu thành công, tôi muốn trả về chuỗi AccessToken, nếu không tôi muốn trả về một thông báo lỗi. Đây là cả hai dây, do đó, không có vấn đề. Nhưng khi kiểm tra giá trị trả về ở phía bên gọi mã, bạn có thể làm điều này hiệu quả như thế nào?

Giống như tôi cần trả về 2 giá trị. Trong trường hợp của một nỗ lực thành công, return = true, "ACESSCODEACXDJGKEIDJ", hoặc nếu nó không thành công, return = false, "Rất tiếc, đã xảy ra lỗi" + ex.ToString();

Sau đó, kiểm tra giá trị trả lại rất dễ dàng (theo lý thuyết). Tôi có thể nghĩ về việc trả về đơn giản là true/false để trả về và sau đó thiết lập biến Session cho các chuỗi.

Cách trả lại nhiều kết quả từ phương thức là gì?

Trả lời

21

Tạo một lớp quả và trả lại mà thay vào đó ...

public class Result 
{ 
    public bool Success {get;set;} 
    public string AccessToken {get;set;} 
    public string ErrorMessage {get;set;} 
} 


public Result GetFacebookToken() 
{ 
    Result result = new Result(); 

    try{ 
     result.AccessToken = "FACEBOOK TOKEN"; 
     result.Success = true; 
    } 
    catch(Exception ex){ 
     result.ErrorMessage = ex.Message; 
     result.Success = false; 
    } 

    return result; 
} 

Sau đó, bạn có thể gọi mã này giống như ...

Result result = GetFacebookToken(); 

if(result.Success) 
{ 
    //do something with result.AccessToken 
} 
else 
{ 
    //do something with result.ErrorMessage 
} 
+0

Cũng có thể làm cho nó phổ biến hơn bất kỳ loại kết quả nào, không phải ju st 'AccessToken' – Alexander

1

Tôi sẽ không trả lại thông báo lỗi. Trả lại một giá trị có ý nghĩa hoặc lỗi và để cho nó bong bóng lên. Cách bạn xử lý lỗi là tùy thuộc vào bạn, nhưng ở mức tối thiểu, tôi sẽ xử lý nó một cách duyên dáng trên giao diện người dùng và ghi nhật ký/thông báo cho ai đó trên chương trình phụ trợ.

Nếu bạn nhấn mạnh vào một cái gì đó trở lại ngay cả khi lỗi chức năng của bạn ra sau đó tôi sẽ trở về một đối tượng mà có các thành viên sau:

Value - String 
Success - Bool 

Sau đó, bạn có thể kiểm tra cho sự thành công và xử lý các giá trị phù hợp.

3

Cách khá hay để thực hiện điều này là trả về một đối tượng bao gồm cả trạng thái Thành công/Thất bại và thông báo lỗi chi tiết.

Cái gì như:

class Result 
{ 
    bool IsSuccessful { get; set; } 
    string DetailedStatus { get; set; } 
} 
8

2 khả năng mùa xuân đến tâm

  1. Sử dụng mô hình TryXXX (được sử dụng trong một số phương pháp BCL như DateTime.TryParse).
  2. Thiết kế một lớp có chứa trạng thái của thao tác và kết quả rồi sau đó đưa phương thức của bạn trở lại lớp này.

Trước hết hãy xem mẫu TryXXX. Về cơ bản nó là một phương thức trả về một giá trị boolean và kết quả là tham số out.

public bool TryXXX(string someInput, out string someResult, out string errorMessage) 
{ 
    ... 
} 

đó sẽ được tiêu thụ như thế này:

string someResult; 
string errorMessage; 
if (!TryXXX("some parameter", out someResult, out errorMessage)) 
{ 
    // an error occurred => use errorMessage to get more details 
} 
else 
{ 
    // everything went fine => use the results here 
} 

Trong cách tiếp cận thứ hai bạn chỉ đơn giản là sẽ thiết kế một lớp sẽ chứa tất cả các thông tin cần thiết:

public class MyResult 
{ 
    public bool Success { get; set; } 
    public string ErrorMessage { get; set; } 

    public string SomeResult { get; set; } 
} 

và sau đó có bạn phương thức trả lại lớp này:

public MyResult MyMethod(string someParameter) 
{ 
    ... 
} 

đó sẽ được tiêu thụ như thế này:

MyResult result = MyMethod("someParameter"); 
if (!result.Success) 
{ 
    // an error occurred => use result.ErrorMessage to get more details 
} 
else 
{ 
    // everything went fine => use the result.SomeResult here 
} 

Tất nhiên kết quả có thể được bất kỳ đối tượng phức tạp khác thay vì (như trong ví dụ này) là một chuỗi.

0

Tại sao bạn không tạo lớp học với 3 thuộc tính. thành công (bool), tin nhắn (chuỗi) và mã thông báo (chuỗi). Bạn có thể tạo một thể hiện của lớp đó, điền các giá trị và trả về nó.

0

Nếu bạn muốn quay trở lại 2 đối tượng, bạn có thể làm một cái gì đó như thế này:

private bool TestThing(out string errorMessage) 
    { 
     bool error = true; 
     if(error) 
     { 
      errorMessage = "This is a message!"; 
      return false; 
     } 

     errorMessage = ""; 
     return true; 
    } 

sau đó bạn sẽ có được bool và thông báo lỗi

1

Bạn chắc chắn chính xác rằng việc sử dụng vị trí lưu trữ bên ngoài (ví dụ: biến phiên) là cách sai.

Cách tiếp cận chính xác tùy thuộc vào việc bạn có coi lỗi là trường hợp đặc biệt không. Nếu không, sau đó làm theo tấm gương đặt trong khuôn khổ bằng cách đặt trước chức năng của bạn với từ Try và có chữ ký của nó trông như thế này:

public bool TryGetFacebookToken(<necessary parameters>, out string token) 
{ 
    ... set the token within the body and return true if it succeeded or false if it did not 
} 

Điều quan trọng cần lưu ý ở đây là phương pháp này thường được sử dụng khi bạn chỉ quan tâm đến việc hoạt động có hiệu quả không (và bạn không thực sự quan tâm đến số lý do tại sao nó không hoạt động nếu nó không thành công) và có một kỳ vọng hợp lý rằng nó có thể không.

Nếu thất bại là ngoại lệ (nghĩa là chương trình được định cấu hình đúng không được gặp phải lỗi này), thì bạn nên sử dụng ngoại lệ. Thực tế, nếu chức năng của bạn không thể thực sự là làm bất kỳ điều gì ngoại trừ bạn đang nhận được, thì không có điểm nào thực sự bắt được nó. Xử lý ngoại lệ thích hợp có nghĩa là cho phép ngoại lệ "bong bóng" lên bất kỳ lớp nào trong chương trình của bạn thực sự có thể làm điều gì đó có ý nghĩa và phù hợp với ngoại lệ.

Điều này cũng đơn giản hóa kịch bản của bạn, vì bạn chỉ cần trả về một chuỗi.

2

Nếu thành công, tôi muốn trả về chuỗi AccessToken, nếu không tôi muốn trả về thông báo lỗi. Đây là cả hai dây, do đó, không có vấn đề. Nhưng khi kiểm tra giá trị trả về ở phía bên gọi mã, bạn có thể làm điều này hiệu quả như thế nào?

C# không thực sự sử dụng thông báo lỗi, chúng tôi sử dụng exceptions. Cách chính xác để làm điều này là chỉ cần ném một ngoại lệ, và để cho người gọi bỏ qua hoặc bắt nó.

Nếu nó không "đặc biệt" để thất bại (ví dụ, nếu một số người dùng có mã thông báo và một số không), thì thay thế sẽ trả về một chuỗi rỗng để cho biết sự vắng mặt của mã thông báo (và vẫn ném ngoại trừ các trường hợp "ngoại lệ" như không thể liên hệ với Facebook, v.v.). Tôi không nghĩ rằng đó là trường hợp của bạn, kể từ khi thất bại ví dụ của bạn bao gồm một đối tượng ngoại lệ.

Điểm mấu chốt, là bạn thường để lại xử lý ngoại lệ (bắt) đến phần trên cùng của ngăn xếp (thường là giao diện người dùng) vì có nhiều ngữ cảnh nhất của thao tác hiện tại. Đó là không sử dụng để bắt một ngoại lệ, định dạng lại một chuỗi, và sau đó trở lại mà thay vào đó - mất thông tin ngoại lệ có giá trị trên đường đi. Chỉ cần để người gọi có ngoại lệ thay vào đó, và họ có thể quyết định cách trình bày sự thất bại đó cho người dùng (hoặc để tiếp tục mà không có tích hợp FB).

này rõ ràng là chế giễu, nhưng hy vọng được quan điểm của tôi trên (mã nói mạnh hơn lời nói):

class Facebook { 
    ... 
    public string GetAccessToken(string username, string password) { 
     // can throw WebException if can't connect to FB 
     this.Connect(); 

     // returns null token if not a Facebook user 
     if (!this.IsUser(username)) return null; 

     // can throw ArgumentException if password is wrong 
     var fbInfo = this.GetInfo(username, password); 

     return fbInfo.AccessToken; 
    } 
    ... 
} 

class Page { 
    void Page_Load(object sender, EventArgs e) { 
     var fb = new Facebook(); 

     string accessToken; 
     try { 
     accessToken = fb.GetAccessToken(this.User.Name, this.txtPassword.Text); 
     } catch (WebException ex) { 
     Log(ex); 
     this.divError.Text = "Sorry, Facebook is down"; 
     // continue processing without Facebook 
     } catch (ArgumentException ex) { 
     // Don't log - we don't care 
     this.divError.Text = "Your password is invalid"; 
     // stop processing, let the user correct password 
     return; 
     } catch (Exception ex) { 
     Log(ex); 
     // Unknown error. Stop processing and show friendly message 
     throw; 
     } 

     if (!string.IsNullOrEmpty(accessToken)) { 
     // enable Facebook integration 
     this.FillFacebookWallPosts(accessToken); 
     } else { 
     // disable Facebook integration 
     this.HideFacebook(); 
     } 
    } 
} 
4

Hãy thử một tuple?

public Tuple<bool, string> ReturnsBoolAndString() { 
    return Tuple.Create(false, "string"); 
} 
6

Để xây dựng dựa trên câu trả lời musefan, tôi thích cùng một khuôn mẫu nhưng có loại quả generic vì vậy tôi có thể sử dụng nó trong suốt toàn bộ codebase:

public class Result 
{ 
    public bool Success { get; set; } 
    public string ErrorMessage { get; set; } 
} 

public class Result<T> : Result 
{ 
    public T Data; 
} 

Một lý do tôi thích vs này ném một ngoại trừ một hàm mà ngược lại trả về dữ liệu, điều này giúp bạn ánh xạ chức năng đó trên một bộ sưu tập, ghi lại các chi tiết ngoại lệ trong các thông báo lỗi, vì vậy bạn không phải lo lắng về một ngoại lệ trên một mục thổi toàn bộ chuỗi. Điều này là tốt cho những tình huống như phân tích dòng ra của một tập tin dữ liệu bằng phẳng, nơi dòng thành công nên di chuyển về phía trước nhưng bất kỳ lỗi nào cần được xử lý riêng lẻ:

public static Result<Thing> ParseThing(string line) 
{ 
    try 
    { 
      // Parse a Thing (or return a parsing error.) 
      return new Result<Thing> { Data = thing, Success = true }; 
    } 
    catch (Exception ex) 
    { 
      return new Result<Thing> { Data = null, Success = false, ErrorMessage = "..." }; 
    } 
} 

... 

var results = lines.Select(ParseThing); 

foreach (var result in results) 
{ 
    // Check result.Success and deal with successes/failures here. 
} 

Tất nhiên, bạn vẫn có thể chọn cách ném một ngoại lệ ra của chức năng đó cho các tình huống thực sự đặc biệt khi thổi lên toàn bộ chuỗi xử lý là những gì bạn muốn.

P.S. Mỗi ngày là ngày tôi muốn C# có nhiều giá trị trả lại.

+0

Thực ra nó hiện có nhiều giá trị trả về.Bạn có thể sử dụng Tuple hoặc Tuple , v.v. Xem https://msdn.microsoft.com/en-us/library/dd268536(v=vs.110).aspx – stefann

+0

Vâng, nhiều giá trị trả lại trực tiếp như một ngôn ngữ xây dựng, theo tinh thần của Lua hoặc Go. (Xin lỗi, không di chuyển các cột mốc.) Một cái gì đó như: điều công cộng, chuỗi ParseThing (...) {...} var thing, err = ParseThing (...); Tôi chưa thực sự nghĩ đến điều đó; Tôi chắc rằng có lý do chính đáng tại sao nó không phù hợp với ngôn ngữ. – user1454265

+0

Tôi biết ý bạn và đồng ý. Nó có thể là cú pháp đường trên đỉnh của cấu trúc Tuple. Bạn nên gửi một đề xuất thông qua Visual Studio. – stefann

2

Một thực hiện chung chung hơn sẽ

C#

public class ReturnMessage<T> 
{ 
    //indicates success or failure of the function 
    public bool IsSuccess { get; set; } 
    //messages(if any) 
    public string Message { get; set; } 
    //data (if any) 
    public T Data { get; set; } 
} 

VB.NET

Public Class ReturnMessage(Of T) 
    'indicates success or failure of the function 
    Public Property IsSuccess As Boolean 
    'messages(if any) 
    Public Property Message As String 
    'data (if any) 
    Public Property Data As T 
End Class 

Bằng phương pháp này người ta có thể vượt qua ex.Message trong khối catch và Data<T> trong khối try

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