ฉันจะออกแบบวิธี TryParse ซึ่งให้ข้อมูลโดยละเอียดในกรณีที่เกิดข้อผิดพลาดในการวิเคราะห์คำได้อย่างไร


9

เมื่อแยกวิเคราะห์อินพุตของผู้ใช้นั้นโดยทั่วไปจะแนะนำให้ไม่โยนและจับข้อยกเว้น แต่แทนที่จะใช้วิธีการตรวจสอบ ใน. NET BCL สิ่งนี้จะเป็นความแตกต่างระหว่าง (เช่นint.Parseการยกเว้นข้อมูลที่ไม่ถูกต้อง) และint.TryParse(ผลตอบแทนfalseจากข้อมูลที่ไม่ถูกต้อง)

ฉันกำลังออกแบบของตัวเอง

Foo.TryParse(string s, out Foo result)

วิธีการและฉันไม่แน่ใจเกี่ยวกับค่าตอบแทน ฉันสามารถใช้boolเหมือนตัวเอง .NET ของTryParseวิธีการ แต่ที่จะให้ข้อบ่งชี้เกี่ยวกับการไม่มีชนิดของข้อผิดพลาดเกี่ยวกับเหตุผลที่แน่นอนว่าทำไม ไม่สามารถแยกวิเคราะห์เป็นs Foo(ตัวอย่างเช่นsอาจมีวงเล็บที่ไม่ตรงกันหรือจำนวนอักขระที่Barไม่ตรงกันหรือ a โดยไม่ต้องสอดคล้องกันBazฯลฯ )

ในฐานะผู้ใช้ API ฉันไม่ชอบวิธีการที่เพิ่งกลับมาประสบความสำเร็จ / ล้มเหลวบูลีนโดยไม่บอกฉันว่าทำไมการดำเนินการล้มเหลว สิ่งนี้ทำให้การดีบั๊กเป็นเกมที่คาดเดาและฉันไม่ต้องการให้ลูกค้าของห้องสมุดของฉันทำเช่นนั้น

ฉันสามารถคิดถึงวิธีแก้ปัญหามากมายสำหรับปัญหานี้ (รหัสสถานะส่งคืนส่งคืนสตริงข้อผิดพลาดเพิ่มสตริงข้อผิดพลาดเป็นพารามิเตอร์ออก) แต่พวกเขาทั้งหมดมีข้อเสียตามลำดับและฉันต้องการให้สอดคล้องกับอนุสัญญาของ Framework ของ

ดังนั้นคำถามของฉันมีดังนี้

มีวิธีใดใน. NET Framework ที่ (a) แยกวิเคราะห์อินพุตโดยไม่ส่งข้อยกเว้นและ (b) ยังคงส่งกลับข้อมูลรายละเอียดข้อผิดพลาดมากกว่าบูลีนจริง / เท็จง่าย ๆ หรือไม่?


1
ลิงก์นั้นไม่สามารถสรุปได้ว่าไม่แนะนำให้โยนและตรวจจับข้อยกเว้น Parse()มีครั้งว่าวิธีที่ดีที่สุดคือการใช้งาน
paparazzo

คำตอบ:


5

ฉันอยากจะแนะนำให้ใช้รูปแบบmonadสำหรับประเภทผลตอบแทนของคุณ

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

โปรดทราบว่ามันไม่ควรเป็นความรับผิดชอบของ Foo ที่จะหาวิธีแยกวิเคราะห์ข้อมูลที่ป้อนโดยผู้ใช้เนื่องจากการผูกเลเยอร์โดเมนของคุณกับเลเยอร์ UI ของคุณโดยตรงและยังละเมิดหลักความรับผิดชอบเดียว

นอกจากนี้คุณยังสามารถสร้างคลาสผลลัพธ์แยกวิเคราะห์เฉพาะFooแทนการใช้ข้อมูลทั่วไปขึ้นอยู่กับกรณีการใช้งานของคุณ

คลาสผลลัพธ์การแยกวิเคราะห์เฉพาะ foo อาจมีลักษณะเช่นนี้:

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

นี่คือรุ่น Monad:

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

ฉันไม่ทราบวิธีการใด ๆ ในกรอบงาน. net ที่ส่งกลับข้อมูลการแยกวิเคราะห์รายละเอียดอย่างละเอียด


ผมเข้าใจความคิดเห็นของคุณเกี่ยวกับชั้น UI ผูกพัน แต่ในกรณีนี้มีอยู่ได้มาตรฐานแทนสายที่ยอมรับของฟูจึงทำให้ความรู้สึกที่มีและFoo.ToString Foo.Parse
Heinzi

และคำถามตัวหนาของฉันคุณสามารถยกตัวอย่างจาก. NET BCL ที่ใช้รูปแบบนี้ได้หรือไม่?
Heinzi

4
Monad เป็นอย่างไร?
JacquesB

@Heinzi: วิธีการใด ๆ ที่ส่งกลับFunc<T>จะเป็นไปตามเกณฑ์นั้นหากคุณรวมอยู่ในTข้อมูลที่คุณต้องการ การส่งคืนข้อมูลข้อผิดพลาดโดยละเอียดนั้นขึ้นอยู่กับคุณเป็นส่วนใหญ่ คุณได้พิจารณาใช้Maybe<T>? ดูmikhail.io/2016/01/monads-explained-in-csharp
Robert Harvey

@JacquesB: ฉันค่อนข้างสงสัยในสิ่งเดียวกัน ลายเซ็นของเมธอดนั้นเข้ากันได้กับพฤติกรรมของ modanic แต่มันเกี่ยวกับมัน
Robert Harvey

1

คุณสามารถดูModelStateในกรอบ MVC มันเป็นตัวแทนของความพยายามในการแยกวิเคราะห์ของการป้อนข้อมูลบางอย่างและอาจมีการรวบรวมข้อผิดพลาด

ที่กล่าวว่าฉันไม่คิดว่าจะมีรูปแบบการเกิดซ้ำใน. net BCL เนื่องจากข้อยกเว้นคือ - ดีขึ้นหรือแย่ลง - รูปแบบที่สร้างขึ้นสำหรับการรายงานเงื่อนไขข้อผิดพลาดใน. net ผมคิดว่าคุณก็ควรจะไปข้างหน้าและดำเนินการแก้ปัญหาของคุณเองคำร้องปัญหาของคุณได้เช่นParseResultระดับสอง subclasses, SuccessfulParseและFailedParseที่SuccessfulParseมีทรัพย์สินที่มีมูลค่าการแยกวิเคราะห์และFailedParseมีคุณสมบัติข้อผิดพลาด การรวมสิ่งนี้เข้ากับการจับคู่รูปแบบใน C # 7 อาจจะดูดีงาม


1

ฉันพบปัญหาที่คล้ายกันโดยต้องการใช้TryParse/Convert/etc.วิธีที่บางครั้งฉันจำเป็นต้องรู้วิธีและสาเหตุที่มันล้มเหลว

ฉันได้รับแรงบันดาลใจจากการที่ serializers จัดการข้อผิดพลาดและการใช้กิจกรรม วิธีนี้ไวยากรณ์สำหรับฉันTryX(..., out T)วิธีดูสะอาดเท่าที่อื่น ๆ และเชื่อถือได้ผลตอบแทนที่เรียบง่ายfalseเป็นรูปแบบที่มีความหมาย

อย่างไรก็ตามเมื่อฉันต้องการรายละเอียดเพิ่มเติมฉันแค่เพิ่มตัวจัดการเหตุการณ์และรับผลลัพธ์อะไรก็ตามที่ฉันต้องการในแพ็คเกจที่ซับซ้อนหรือเรียบง่ายตามที่ฉันต้องการ ( MyEventArgsด้านล่าง) เพิ่มลงในรายการสตริงเพิ่มExceptionDispatchInfoและจับข้อยกเว้น; ให้ผู้โทรตัดสินใจว่าจะจัดการกับสิ่งใดที่ผิดพลาดหรือไม่

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;}
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.