Ayrıştırma hatası durumunda ayrıntılı bilgi sağlayan bir TryParse yöntemini nasıl tasarlayabilirim?


9

Kullanıcı girişini ayrıştırırken genellikle istisnaları atmamak ve yakalamak değil, daha ziyade doğrulama yöntemlerini kullanmanız önerilir. .NET BCL'de bu, örneğin int.Parse(geçersiz veriler için bir istisna atar) ve int.TryParse( falsegeçersiz verilerde geri dönüşler ) arasındaki fark olacaktır.

Kendim tasarlıyorum

Foo.TryParse(string s, out Foo result)

yöntemi ve dönüş değeri hakkında emin değilim. bool.NET'in kendi TryParseyöntemi gibi kullanabilirim , ancak bu hata türü hakkında, neden s ayrıştırılamamasının kesin nedeni hakkında hiçbir belirti vermez Foo. (Örneğin, seşleşmeyen parantez veya yanlış sayıda karakter veya Barkarşılık gelen bir Bazvb. Olabilir .)

Bir itibariyle kullanıcı API'larından, şiddetle Bana haber vermeden bir başarı / başarısızlık Boole dönüş yöntemleri sevmediğim neden işlem başarısız oldu. Bu hata ayıklama bir tahmin oyunu yapar ve ben de bunu kütüphanemin müşterilerine dayatmak istemiyorum.

Ben bu soruna geçici çözümler bir çok düşünebilirsiniz (dönüş durum kodları, bir hata dizesi döndürmek, bir çıkış dizesi olarak bir hata dizesi eklemek), ama hepsi kendi olumsuz yanları var, ve ben de kuralları ile tutarlı kalmak istiyorum .NET Framework .

Dolayısıyla sorum şu:

.NET Framework'te (a) özel durumları atmadan girdiyi ayrıştırma ve (b) hala basit bir doğru / yanlış Boole'den daha ayrıntılı hata bilgileri döndüren yöntemler var mı?


1
Bu bağlantı, istisnaları atmanız ve yakalamanız önerilmez. Kullanmanın en iyi yolunun zamanları vardır Parse().
paparazzo

Yanıtlar:


5

Dönüş türünüz için monad desenini kullanmanızı tavsiye ederim .

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

Ayrıca, etki alanı katmanınızı doğrudan kullanıcı arayüzü katmanınıza bağladığından ve tek sorumluluk ilkesini ihlal ettiğinden, kullanıcı girişinden nasıl ayrıştırılması gerektiğini Foo'nun sorumluluğu olmamalıdır.

Ayrıca Foo, kullanım durumunuza bağlı olarak jenerikleri kullanmak yerine ayrıştırma sonuç sınıfını özel yapabilirsiniz .

Foo'ya özel bir ayrıştırma sonucu sınıfı aşağıdaki gibi görünebilir:

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

İşte Monad sürümü:

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

Ayrıntılı ayrıştırma hatası bilgileri döndüren .net çerçevesinde herhangi bir yöntem farkında değilim.


Ben UI katmanı bağlama hakkındaki övgünün Anlıyorum, ama olması mantıklı bu durumda, Foo bir standardize kanonik dize temsilini vardır Foo.ToStringve Foo.Parse.
Heinzi

Ve cesur sorumla birlikte, bana bu kalıbı kullanan .NET BCL'den bir örnek verebilir misiniz?
Heinzi

4
Bu nasıl bir monad?
JacquesB

@Heinzi: İhtiyacınız olan bilgilere eklerseniz , döndüren herhangi bir yöntem bu Func<T>ölçütleri Tkarşılar. Ayrıntılı hata bilgilerinin geri gönderilmesi büyük ölçüde size bağlıdır. Bir kullanmayı düşündünüz mü Maybe<T>? Bkz. Mikhail.io/2016/01/monads-explained-in-csharp
Robert Harvey

@JacquesB: Aynı şeyi merak ediyordum. Yöntem imzası, modanik davranışla uyumludur, ancak hepsi bu kadar.
Robert Harvey

1

ModelState'e MVC çerçevesinde bakabilirsiniz . Bazı girdilerin ayrıştırma girişimini temsil eder ve bir hata koleksiyonu içerebilir.

Bununla birlikte, .net BCL'de bunun için yinelenen bir model olduğunu düşünmüyorum, çünkü istisnalar - daha iyi veya daha kötü - .net'teki hata koşullarını bildirmek için belirlenmiş bir modeldir. Ben sadece devam edin ve örneğin bir sorununuzu suiting kendi çözümünüzü uygulamak gerektiğini düşünüyorum ParseResultiki alt sınıf içeren sınıf SuccessfulParseve FailedParsenerede, SuccessfulParseçözümlü değere sahip bir özelliği vardır ve FailedParsebir hata mesajı özelliğine sahiptir. Bunu C # 7'deki desen eşleşmesi ile birleştirmek oldukça zarif olabilir.


1

Bazen nasıl ve neden başarısız olduğunu bilmem gereken bir TryParse/Convert/etc.yöntem kullanmak isteyen ile benzer sorunlarla karşılaştım .

Bazı serileştiricilerin hataları nasıl ele aldığından ve olayları kullandığından ilham aldım. Bu şekilde, yöntemimin sözdizimi TryX(..., out T)diğeri kadar temiz görünür falseve desenin ima ettiği gibi basit bir şekilde güvenilir bir şekilde döndürür .

Ancak, daha fazla ayrıntıya ihtiyaç duyduğumda, sadece bir Olay İşleyicisi eklerim ve istediğim sonuçları, istediğim kadar karmaşık veya basit bir pakette elde ederim ( MyEventArgsaşağıda). Bir dizeler listesine ekleyin, ekleyin ExceptionDispatchInfove İstisnalar yakalayın; arayanın yanlış giden herhangi bir şeyle başa çıkıp çıkmayacağına ve nasıl başa çıkacağına karar vermesine izin verin.

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;}
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.