3

フロー制御に例外を使用し、例外的な状況にのみ例外を使用するのは悪いことだと人々が言うことは知っていますが、ブロック全体をtry-catch

たとえばTextBox、ユーザーが入力を入力してキーと値のような方法で解析できるダイアログウィンドウがあるとします。

私はこの正確な状況を処理しなければならないコードを継承しているので、この状況はあなたが思うほど不自然ではありません(家畜ではありませんが)。

このコードの壁を考えてみましょう。

class Animals
{
    public int catA, catB;
    public float dogA, dogB;
    public int mouseA, mouseB, mouseC;
    public double cow;
}

class Program
{
    static void Main(string[] args)
    {
        string input = "Sets all the farm animals CAT 3 5 DOG 21.3 5.23 MOUSE 1 0 1 COW 12.25";

        string[] splitInput = input.Split(' ');

        string[] animals = { "CAT", "DOG", "MOUSE", "COW", "CHICKEN", "GOOSE", "HEN", "BUNNY" };

        Animals animal = new Animals();

        for (int i = 0; i < splitInput.Length; i++)
        {
            string token = splitInput[i];

            if (animals.Contains(token))
            {
                switch (token)
                {
                    case "CAT":
                        animal.catA = int.Parse(splitInput[i + 1]);
                        animal.catB = int.Parse(splitInput[i + 2]);
                        break;
                    case "DOG":
                        animal.dogA = float.Parse(splitInput[i + 1]);
                        animal.dogB = float.Parse(splitInput[i + 2]);
                        break;
                    case "MOUSE":
                        animal.mouseA = int.Parse(splitInput[i + 1]);
                        animal.mouseB = int.Parse(splitInput[i + 2]);
                        animal.mouseC = int.Parse(splitInput[i + 3]);
                        break;
                    case "COW":
                        animal.cow = double.Parse(splitInput[i + 1]);
                        break;
                }
            }
        }
    }
}

実際には、それよりもはるかに多くの家畜と取り扱いがあります。しかし、多くのことがうまくいかない可能性があります。ユーザーが間違った数のパラメーターを入力する可能性があります。ユーザーが間違った形式で入力を入力する可能性があります。ユーザーは、データ型が処理するには大きすぎるまたは小さすぎる数値を指定できます。

これらのさまざまなエラーはすべて、を使用して例外なく処理できTryParseます。ユーザーが特定の動物に使用しようとしたパラメーターの数をチェックし、パラメーターがデータ型に対して大きすぎるか小さすぎるかをチェックします(TryParseは0を返すため)。しかし、誰もが同じ結果になるはずです:

入力したMessageBoxデータが無効であることをユーザーに通知し、修正するように表示されます。上司は、エラーごとに異なるメッセージボックスを望んでいません。それでは、すべてを行う代わりに、ブロックをaでラップtry-catchし、catchステートメントでそのエラーメッセージボックスを表示して、ユーザーに再試行させてはどうでしょうか。

これは最良の例ではないかもしれませんが、そうでなければ単一のtry-catchの代わりに使用できる大量のエラー処理が存在する他のシナリオを考えてみてください。それはより良い解決策ではありませんか?

4

3 に答える 3

3

Try-Catch-Finalizeの使用は、TryParseの使用とは異なります。TryParseはコードの実行を停止せず、例外をスローする代わりに、エラーをスキップして変数を初期化しないままにします。

安全なコードを書くことは、エラー処理や「多くの場合、うまくいかない可能性がある」よりもはるかに重要です。つまり、制限が役立つ場合です。適切な実行と良好な結果を得るために、ユーザーの自由を制限します。次のステップはエラー処理です。

于 2012-11-03T15:22:44.577 に答える
0

これは、エラーメッセージをどの程度制御する必要があるかによって異なります。「キャッチオール」アプローチを使用すると、「問題が発生しました。再試行してください」ということだけをユーザーに通知できます。

1つの大きなtry-catchブロックを使用してキャッチExceptionすると、int解析に完全に関連するのではなく、コード内の他のエラーが非表示になります。少なくとも、予想される例外の種類を絞り込むようにしてください。

また、残りの解析を続行できることも考慮に入れてTryParseください。つまり、すべての問題を追跡して記録し、後で詳細な方法でユーザーに通知することができます。それはあなたが達成しようとしていることに依存します。

于 2012-11-03T15:26:18.867 に答える
0

楽しみのために、私はあなたのケースの簡単なパーサーコンビネーターのような例を一緒に投げました。半複雑な構文の解析に関しては、パーサーコンビネーターは非常に強力です。さらに、パーサーコンビネーターライブラリを拡張して、パーサーが失敗した理由を人間が読み取れるエラーをサポートすることができます。

おそらくそれはあなたがタスクに対処するためのさまざまな方法についてあなたにアイデアを与えることができます。

一部のパーサープリミティブを使用すると、「猫」を解析すると次のようになります。

static IParseResult<Cat> ParseCat(this ParserState ps)
{
    var value = new Cat();
    var result = 
            ps.AttemptTo (() => ps.ParseToken("CAT"))
        &&  ps.AttemptTo (ps.ParseDouble, out value.A)
        &&  ps.AttemptTo (ps.ParseDouble, out value.B)
        ;

    return ps.Result(result, value);
}

犬、牛、マウスについても同様です。動物の解析は、これらすべての組み合わせです。

    static IParseResult<Animal> ParseAnimal(this ParserState ps)
    {
        Animal value = null;
        var result =
                ps.AttemptTo(ps.ParseCat    , out value)
            ||  ps.AttemptTo(ps.ParseDog    , out value)
            ||  ps.AttemptTo(ps.ParseMouse  , out value)
            ||  ps.AttemptTo(ps.ParseCow    , out value)
            ||  ps.AttemptTo(ps.ParseUnknown, out value)
            ;

        return ps.Result(result, value);
    }

最後に、入力をできるだけ多くの動物と解析したいと思います。

    public static IParseResult<Animal[]> ParseAnimals(this ParserState ps)
    {
        Animal[] value;
        var result = ps.AttemptMany(ps.ParseAnimal, out value);
        return ps.Result(result, value);
    }

完全なサンプル:

namespace ParseTest
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Threading;
    using System.Linq;

    static class Program
    {

        static void Main(string[] args)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            var input = "Sets all the farm animals CAT 3 5 DOG 21.3 5.23 MOUSE 1 0 1 COW 12.25";

            Console.WriteLine(input);

            var ps = new ParserState(input.Split(' '));

            var result = ps.ParseAnimals();

            if (result.Success)
            {
                foreach (var animal in result.Value.Where(v => !(v is Unknown)))
                {
                    Console.WriteLine(animal);
                }
            }
            else
            {
                Console.WriteLine("Parse failure");
            }

        }
    }


    // ParserState tracks where we are in the input
    class ParserState
    {
        public readonly string[] Tokens;
        public int CurrentPosition;

        public ParserState(string[] tokens) 
        {
            Tokens = tokens ?? new string[0];
        }


        public string Peek
        {
            get
            {
                if (CurrentPosition < 0)
                {
                    return "";
                }

                if (CurrentPosition >= Tokens.Length)
                {
                    return "";
                }

                return Tokens[CurrentPosition];
            }
        }

        public string PeekAndAdvance ()
        {
            var result = Peek;

            ++CurrentPosition;

            return result;
        }

        public bool EndOfStream
        {
            get { return CurrentPosition >= Tokens.Length; }
        }

    }
    struct Empty
    {

    }

    // ParseResult are the result of parsers, it might be a succcess or failure
    interface IParseResult<out T>
    {
        ParserState State { get; }
        bool Success { get; }
        T Value { get; }
    }

    class ParseResult<T> : IParseResult<T>
    {
        readonly ParserState m_state;
        readonly bool m_success;
        readonly T m_value;

        public ParseResult(ParserState state, bool success, T value)
        {
            m_state = state;
            m_success = success;
            m_value = value;
        }

        public ParserState State
        {
            get { return m_state; }
        }

        public bool Success
        {
            get { return m_success; }
        }

        public T Value 
        {
            get { return m_value; }
        }
    }

    static class ParserExtensions
    {
        public static IParseResult<T> Result<T>(this ParserState ps, bool success, T value)
        {
            return new ParseResult<T>(ps, success, value);
        }

        public static IParseResult<T> Success<T>(this ParserState ps, T value)
        {
            return ps.Result(true, value);
        }

        public static IParseResult<T> Failure<T>(this ParserState ps)
        {
            return ps.Result(true, default(T));
        }

        public static bool AttemptTo<T>(this ParserState ps, Func<IParseResult<T>> action)
        {
            T value;
            return ps.AttemptTo(action, out value);
        }

        // Attempts to run a parser, 
        // if the parser fails the parser state is returned to its previous state
        public static bool AttemptTo<T>(this ParserState ps, Func<IParseResult<T>> action, out T value)
        {
            value = default(T);
            if (ps.EndOfStream)
            {
                return false;
            }

            var currentPos = ps.CurrentPosition;

            var result = action();

            if (result.Success)
            {
                value = result.Value;
            }
            else
            {
                ps.CurrentPosition = currentPos;
            }

            return result.Success;
        }

        // Attempts to run a parser many times and build an array of values, 
        // if the parser fails the parser state is returned to its previous state
        // and the result is returned
        public static bool AttemptMany<T>(this ParserState ps, Func<IParseResult<T>> action, out T[] values)
        {
            values = new T[0];
            if (ps.EndOfStream)
            {
                return true;
            }

            var parsedValues = new List<T>();

            var currentPos = ps.CurrentPosition;

            IParseResult<T> result;

            while ((result = action ()).Success)
            {
                currentPos = ps.CurrentPosition;
                parsedValues.Add(result.Value);
            }

            ps.CurrentPosition = currentPos;

            values = parsedValues.ToArray();

            return true;
        }

        static IParseResult<Empty> ParseToken(this ParserState ps, string token)
        {
            var value = new Empty();
            var result = ps.PeekAndAdvance().Equals(token, StringComparison.OrdinalIgnoreCase);
            return ps.Result(result, value);
        }

        static IParseResult<double> ParseDouble(this ParserState ps)
        {
            double value;
            var result = double.TryParse(ps.PeekAndAdvance(), out value);
            return ps.Result(result, value);
        }

        static IParseResult<Cat> ParseCat(this ParserState ps)
        {
            var value = new Cat();
            var result = 
                    ps.AttemptTo (() => ps.ParseToken("CAT"))
                &&  ps.AttemptTo (ps.ParseDouble, out value.A)
                &&  ps.AttemptTo (ps.ParseDouble, out value.B)
                ;

            return ps.Result(result, value);
        }

        static IParseResult<Dog> ParseDog(this ParserState ps)
        {
            var value = new Dog();
            var result =
                    ps.AttemptTo(() => ps.ParseToken("DOG"))
                &&  ps.AttemptTo(ps.ParseDouble, out value.A)
                &&  ps.AttemptTo(ps.ParseDouble, out value.B)
                ;

            return ps.Result(result, value);
        }

        static IParseResult<Mouse> ParseMouse(this ParserState ps)
        {
            var value = new Mouse();
            var result =
                    ps.AttemptTo(() => ps.ParseToken("MOUSE"))
                &&  ps.AttemptTo(ps.ParseDouble, out value.A)
                &&  ps.AttemptTo(ps.ParseDouble, out value.B)
                &&  ps.AttemptTo(ps.ParseDouble, out value.C)
                ;

            return ps.Result(result, value);
        }

        static IParseResult<Cow> ParseCow(this ParserState ps)
        {
            var value = new Cow();
            var result =
                    ps.AttemptTo(() => ps.ParseToken("COW"))
                &&  ps.AttemptTo(ps.ParseDouble, out value.A)
                ;

            return ps.Result(result, value);
        }

        static IParseResult<Unknown> ParseUnknown(this ParserState ps)
        {
            ps.PeekAndAdvance();
            return ps.Success(new Unknown());
        }

        static IParseResult<Animal> ParseAnimal(this ParserState ps)
        {
            Animal value = null;
            var result =
                    ps.AttemptTo(ps.ParseCat    , out value)
                ||  ps.AttemptTo(ps.ParseDog    , out value)
                ||  ps.AttemptTo(ps.ParseMouse  , out value)
                ||  ps.AttemptTo(ps.ParseCow    , out value)
                ||  ps.AttemptTo(ps.ParseUnknown, out value)
                ;

            return ps.Result(result, value);
        }

        public static IParseResult<Animal[]> ParseAnimals(this ParserState ps)
        {
            Animal[] value;
            var result = ps.AttemptMany(ps.ParseAnimal, out value);
            return ps.Result(result, value);
        }
    }

    class Animal
    {

    }

    class Cat : Animal
    {
        public double A;
        public double B;

        public override string ToString()
        {
            return new {Type = "CAT", A, B}.ToString();
        }
    }

    class Dog : Animal
    {
        public double A;
        public double B;

        public override string ToString()
        {
            return new { Type = "DOG", A, B }.ToString();
        }
    }

    class Mouse : Animal
    {
        public double A;
        public double B;
        public double C;

        public override string ToString()
        {
            return new { Type = "MOUSE", A, B, C }.ToString();
        }
    }

    class Cow : Animal
    {
        public double A;

        public override string ToString()
        {
            return new { Type = "COW", A}.ToString();
        }
    }

    class Unknown : Animal
    {
        public override string ToString()
        {
            return new { Type = "UNKNOWN"}.ToString();
        }
    }

}

PS。FParsecは最後です。.NETに最適なパーサーコンビネーターライブラリを確認しました:http ://www.quanttec.com/fparsec/tutorial.html

PS。MicroParserはFParsecのC#バリアントですが、巨大な文字列を解析する機能を犠牲にして、より小さくすることを目的としています:http: //microparser.codeplex.com/

于 2012-11-03T16:57:17.220 に答える