Làm cách nào để thiết kế một phương thức TryPude cung cấp thông tin chi tiết trong trường hợp có lỗi phân tích cú pháp?


9

Khi phân tích cú pháp đầu vào của người dùng, thông thường không nên ném và bắt ngoại lệ mà nên sử dụng các phương thức xác thực. Trong .NET BCL, đây sẽ là sự khác biệt giữa, ví dụ, int.Parse(ném ngoại lệ vào dữ liệu không hợp lệ) và int.TryParse(trả falsevề dữ liệu không hợp lệ).

Tôi đang thiết kế của riêng tôi

Foo.TryParse(string s, out Foo result)

phương pháp và tôi không chắc chắn về giá trị trả về. Tôi có thể sử dụng boolnhư TryParsephương thức riêng của .NET , nhưng điều đó sẽ không đưa ra dấu hiệu nào về loại lỗi, về lý do chính xác tại sao s không thể phân tích cú pháp thành một Foo. (Ví dụ: scó thể có dấu ngoặc đơn chưa từng có hoặc số ký tự sai hoặc Barkhông có ký tự tương ứng Baz, v.v.)

người dùng API, tôi không thích các phương thức chỉ trả về Boolean thành công / thất bại mà không cho tôi biết lý do tại sao thao tác thất bại. Điều này làm cho việc gỡ lỗi một trò chơi đoán và tôi cũng không muốn áp đặt điều đó lên các khách hàng của thư viện của mình.

Tôi có thể nghĩ ra rất nhiều cách giải quyết cho vấn đề này (trả về mã trạng thái, trả về chuỗi lỗi, thêm chuỗi lỗi làm tham số out), nhưng tất cả chúng đều có nhược điểm tương ứng và tôi cũng muốn tuân thủ các quy ước của .NET Framework .

Vì vậy, câu hỏi của tôi là như sau:

Có các phương thức trong .NET Framework mà (a) phân tích cú pháp đầu vào mà không đưa ra ngoại lệ và (b) vẫn trả về thông tin lỗi chi tiết hơn so với Boolean đúng / sai đơn giản?


1
Liên kết đó không kết luận rằng không nên ném và bắt ngoại lệ. Có những lúc cách tốt nhất là sử dụng Parse().
paparazzo

Câu trả lời:


5

Tôi sẽ khuyên bạn nên sử dụng mẫu đơn cho kiểu trả về của bạn.

ParseResult<Foo> foo = FooParser.Parse("input");

Cũng lưu ý, Foo không có trách nhiệm tìm ra cách phân tích cú pháp từ đầu vào của người dùng vì điều này liên kết trực tiếp lớp miền của bạn với lớp UI của bạn và cũng vi phạm nguyên tắc trách nhiệm duy nhất.

Bạn cũng có thể tạo một lớp kết quả phân tích cụ thể Foothay vì sử dụng thuốc generic, tùy thuộc vào trường hợp sử dụng của bạn.

Một lớp kết quả phân tích cụ thể foo có thể trông giống như thế này:

class FooParseResult
{
     Foo Value { get; set; }
     bool PassedRequirement1 { get; set; }
     bool PassedRequirement2 { get; set; }
}

Đây là phiên bản Monad:

class ParseResult<T>
{
     T Value { get; set; }
     string ParseErrorMessage { get; set; }
     bool WasSuccessful { get; set; }
}

Tôi không biết bất kỳ phương thức nào trong khung .net trả về thông tin lỗi phân tích chi tiết.


Tôi hiểu nhận xét của bạn về liên kết lớp UI, nhưng trong trường hợp này tồn tại một biểu diễn chuỗi chuẩn, hợp quy của Foo, vì vậy nó có ý nghĩa để có Foo.ToStringFoo.Parse.
Heinzi

Và, viết câu hỏi in đậm của tôi, bạn có thể cho tôi một ví dụ từ .NET BCL sử dụng mẫu này không?
Heinzi

4
Làm thế nào mà là một đơn nguyên?
JacquesB

@Heinzi: Bất kỳ phương thức nào trả về a Func<T>sẽ đáp ứng tiêu chí đó, nếu bạn đưa vào Tthông tin bạn cần. Trả lại thông tin lỗi chi tiết phần lớn là tùy thuộc vào bạn. Bạn đã xem xét sử dụng một Maybe<T>? Xem mikhail.io/2016/01/monads-explained-in-csharp
Robert Harvey

@JacquesB: Tôi hơi thắc mắc điều tương tự. Chữ ký phương thức tương thích với hành vi modanic, nhưng đó là về nó.
Robert Harvey

1

Bạn có thể xem ModelState trong khung MVC. Nó đại diện cho một phân tích cố gắng của một số đầu vào và có thể có một tập hợp các lỗi.

Điều đó nói rằng, tôi không nghĩ rằng có một mẫu lặp lại cho điều này trong BCL .net, vì các trường hợp ngoại lệ là - tốt hơn hoặc xấu hơn - mẫu được thiết lập để báo cáo các điều kiện lỗi trong .net. Tôi nghĩ bạn chỉ nên tiếp tục và thực hiện giải pháp của riêng mình phù hợp với vấn đề của bạn, ví dụ một ParseResultlớp có hai lớp con SuccessfulParseFailedParse, nơi SuccessfulParsecó một thuộc tính có giá trị được phân tích cú pháp và FailedParsecó thuộc tính thông báo lỗi. Kết hợp điều này với kết hợp mẫu trong C # 7 có thể khá thanh lịch.


1

Tôi đã gặp phải những vấn đề tương tự với việc muốn sử dụng một TryParse/Convert/etc.phương pháp mà đôi khi tôi cần biết làm thế nào và tại sao nó lại thất bại.

Cuối cùng tôi đã lấy cảm hứng từ cách một số serial serial xử lý lỗi và sử dụng các sự kiện. Bằng cách này, cú pháp cho TryX(..., out T)phương thức của tôi trông sạch sẽ như bất kỳ phương thức nào khác và trả về một cách đơn giản falsenhư mẫu ngụ ý.

Tuy nhiên, khi tôi muốn biết thêm chi tiết, tôi chỉ cần thêm Trình xử lý sự kiện và nhận bất kỳ kết quả nào tôi cần trong một gói phức tạp hoặc đơn giản như tôi muốn ( MyEventArgsbên dưới). Thêm nó vào danh sách các chuỗi, thêm ExceptionDispatchInfovà bắt Ngoại lệ; để người gọi quyết định xem và làm thế nào nó muốn đối phó với bất cứ điều gì sai.

public class Program
{
    public static void Main()
    {
        var c = new MyConverter();

        //here's where I'm subscibing to errors that occur
        c.Error += (sender, args) => Console.WriteLine(args.Details);

        c.TryCast<int>("5", out int i);
    }
}

//here's our converter class
public class MyConverter
{
    //invoke this event whenever something goes wrong and fill out your EventArgs with details
    public event EventHandler<MyEventArgs> Error;

    //intentionally stupid implementation
    public bool TryCast<T>(object input, out T output)
    {
        bool success = true;
        output = default (T);

        //try-catch here because it's an easy way to demonstrate my example
        try
        {
            output = (T)input;
        }
        catch (Exception ex)
        {
            success = false;
            Error?.Invoke(this, new MyEventArgs{Details = ex.ToString()});
        }

        return success;
    }
}

//stores whatever information you want to make available
public class MyEventArgs : EventArgs
{
    public string Details {get; set;}
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.