10

ユーザー入力を検証するベスト プラクティスの方法はありますか?

実際の問題:

ユーザーはウィンドウで特定の入力を行います。これらの入力が完了したら、[作成] をクリックします。これで、すべての無効な入力を含むポップアップ メッセージが表示されるはずです。無効な入力がない場合は、そのまま続行します。

Form クラスでこれを簡単に行うことができます。しかし、設定されたプロパティで入力を検証するベストプラクティスの方法を覚えています。問題は、この方法で検証した場合、そのクラスのインスタンスを既に作成している (または、プロパティを設定できない ;) ことです。入力が有効でない限り、クラスのインスタンスは作成されません。

すべての errorMessages を配置できるリストを含む ErrorMessages クラスを作成することを計画していました。無効な入力が与えられるたびに、新しいメッセージが errorMessages リストに追加されます。したがって、ユーザーが「作成」ボタンをクリックすると、リスト内のすべてのメッセージが表示されます。これは物事を処理する良い方法ですか?

それで、ベストプラクティスの方法はありますか?そのようなソリューションを提供する設計パターンはありますか?

編集:これは学校の課題です。したがって、非論理的な要件があります。「作成」をクリックしたときに、無効な入力をすべて表示する必要があります。これを Form クラスの外で行いたいと思います。(そのため、検証は GUI がなくても機能します。この時点では、まだ GUI を作成していません)。まず、機能が正しく動作することを確認します;)。私は自分のコードをクリーンで抽象的で OOP に保ちたいと思っています。では、エラー メッセージを表示するにはどうすればよいでしょうか。

4

4 に答える 4

8

すべてのerrorMessageを配置できるリストを含むErrorMessagesクラスを作成することを計画していました。無効な入力が与えられるたびに、新しいメッセージがerrorMessagesリストに追加されます。したがって、ユーザーが「作成」ボタンをクリックすると、リスト内のすべてのメッセージが表示されます。これは物事を処理する良い方法ですか?

主観的には、ユーザーが入力した値が無効であるというフィードバックを即座に提供する方がよいと思います。そうすれば、彼らはすぐに戻ってそれを修正することができます。

つまり、考えてみてください。あなたが提案するアプローチは、文字通り彼らに問題の巨大なリストを与えるでしょう、それはあまりユーザーフレンドリーではありません。さらに、それらの問題をすべて覚えて、一度に1つずつ戻って修正できるようにするにはどうすればよいでしょうか。(ヒント:そうではありません。)

代わりに、ErrorProviderクラスを使用して、適切なコントロールのすぐ横にエラーを表示することをお勧めします。私はここここで私の答えの中でこのアプローチについてもう少し話しました。

もちろん、最終的な送信([OK] / [送信]ボタンをクリック)時にすべての入力が有効であることを確認する必要がありますが、それはエラーの有無を確認する単純なケースです。

Formクラスでこれを簡単に行うことができます。しかし、設定されたプロパティの入力を検証するためのいくつかのベストプラクティスの方法を覚えています。

はい、ここでのアイデアはカプセル化です。Formクラスは、フォームに関することだけを知っている必要があります。さまざまなコントロールすべてに対して、どのような種類の入力が有効であるか、または無効であるかを知る必要はありません。

代わりに、この検証ロジックは、データを格納するクラスなど、他の場所に配置する必要があります。そのクラスは、データを取得および設定するためのパブリックプロパティを公開し、setterメソッド内でデータを検証します。

つまり、Formが実行する必要があるのは、データクラスのsetterメソッドを呼び出すことだけです。データクラスはそのすべてを処理するため、フォームはデータの検証方法やデータの意味について何も知る必要はありません。

これは発生しないはずです。入力が有効でない限り、クラスのインスタンスを作成することはできません。

これが実際に当てはまる場合は、必要なすべてのデータをパラメーターとして受け入れるクラスのコンストラクターを提供する必要があります。コンストラクターの本体は、指定されたデータを検証し、いずれかが無効な場合は例外をスローします。例外はクラスの作成を防ぎ、無効なデータを含むクラスのインスタンスが存在しないようにします。

このようなクラスには、おそらくsetterメソッドはまったくなく、getterのみが含まれます。

ただし、これはC#の世界では珍しい要件です(ただし、C ++では一般的です)。一般に、検証コードをセッター内に配置すると問題なく機能します。

私のプロパティにはいくつかのプライベートセッターがあります。したがって、それらは私のデータクラスのコンストラクターでのみ設定されます。問題は、これが私の検証を容易にしないように見えることです

なぜそれが何かを変えるのでしょうか?プライベートセッター内で検証を処理します。検証が失敗した場合は、例外をスローします。コンストラクターは例外を処理しないため、オブジェクトをインスタンス化しようとしたコードに、そのメソッドからバブリングを続けます。そのコードが例外を処理したい場合(たとえば、ユーザーにエラーメッセージを表示したい場合)、そうすることができます。

確かに、無効な入力の場合に例外をスローすることは、必ずしも「ベストプラクティス」ではありません。その理由は、例外は通常、予期しない状況に備えて予約する必要があり、ユーザーが失敗して無効なデータを提供することは当然のことです。でも:

  1. コンストラクターは値を返すことができないため、これはコンストラクター内のデータ検証に使用できる唯一のオプションです。
  2. 最新のコンピューターは、ユーザーが画面上の変更を認識できるよりも速く例外を処理できるため、UIコードでは、例外処理のコストは基本的にごくわずかです。
于 2013-03-15T10:56:22.957 に答える
3

これは単純な要件ですが、時々議論されています。これは、検証を処理する私の「現在の」アプローチです。私はまだこのアプローチを使用していません。これは単なる概念です。このアプローチはさらに開発する必要があります

まず、カスタム検証属性を作成します

public class ValidationAttribute : Attribute{
  public type RuleType{get;set;}
  public string Rule{get;set;}
  public string[] RuleValue{get;set;}
}

次に、カスタム エラー ハンドラ/メッセージを作成します。

public class ValidationResult{
  public bool IsSuccess{get;set;};
  public string[] ErrorMessages{get;set;};
}

次に、バリデータを作成します

public class RuleValidator{
  public ValidationResult Validate(object o){
    ValidationResult result = new ValidationResult();
    List<string> validationErrors = new List<string>();
    PropertyInfo[] properties = o.GetType().GetProperties();
    foreach(PropertyInfo prop in properties){
      // validate here
      // if error occur{
        validationErrors.Add(string.Format("ErrorMessage at {0}", prop.Name));
      //}
    }

    result.ErrorMessages = validationErrors.ToArray();
  }
}

それを使用するには、次のようにします。

public class Person{
  [ValidationAttribute(typeof(string), "Required", "true")]
  public string Name{get;set;}

  [ValidationAttribute(typeof(int), "Min", "1")]
  public int Age{get;set;}
}

バリデータを呼び出すには

public void ValidatePerson(Person person){
  RuleValidator validator = new RuleValidator();
  ValidationResult result = validator.Validate(person);
  // generate the error message here, use result.ErrorMessages as source
}

利点は何ですか:

  1. 任意のアプリケーション プラットフォーム (Winforms、Asp.Net、WCF など) で使用できます。
  2. 属性レベルでルールを設定できます
  3. 自動検証を行うことができます
  4. このアプローチは、検証ロジックを分離するために、カスタム バリデーターを含む DependencyInjection と共に使用できます。

不利な点:

  1. バリデータの作成が難しい
  2. うまく処理しないと、バリデーターの数が非常に多くなる可能性があります
  3. リフレクションの使用によるパフォーマンスの低下
于 2013-03-15T11:28:42.753 に答える
2

クラスを参照してくださいErrorProvider(ドキュメントはこちら)。これは、ほとんどの標準 WinForms コントロールにアタッチできる一連の標準ビジュアル インジケーターを提供します。

于 2013-03-15T10:54:27.607 に答える
0

いくつかの可能なアプローチがあります:

  • 「インスタント」検証を使用します。

ユーザーが値を入力すると、入力中にチェックされ(TextChanged)、すぐに検証されます。新しいクラスのインスタンスを作成し、プロパティ/メソッドを呼び出して、受け入れstringて返す(またはプロパティの場合はboolスローする)必要があります-特別なエラー条件(テキストボックスの横にある赤いラベル、何かが点滅している、またはあなたが言うべきことを何でもできること)を描画しますユーザー「間違っています!」)。ExceptionfalseErrorProvider

これは私が使用するのが好きですが、少し異なります。通常、私はチェックTypeしてから、フォームですぐに解析しようとします。stringフォームが'sで動作し、すべてのフォーマットと検証がクラス(プロパティセッター)で行われる場合は、さらに抽象化することができます。または、フォームに追加情報を(クエリメソッドまたは属性を使用して)提供できるため、クラスをインスタンス化したり、セッターを使用したりすることなく、フォームを即座に検証できます。例として、double factorプロパティをフォーム(またはコントロール)で識別して、'double.Parse and you can have attributeDefaultValue which can be used to display to the user value in the different way when it's different from default (like it is done byPropertyGrid`)を実行できます。

  • 通常の検証を使用します。

ユーザーが入力を終了したら、検証します(値を設定して例外をキャッチします)。間違っている場合は、ESC(変更をキャンセルするため)を押すか、入力を修正して検証に合格するまで、ユーザーは「終了」または「進行」できません。

これは嫌いです。ユーザーを保持するという考えは私(そしてユーザーofc)を苛立たせます。また、クロスチェックを実装するのは困難です(値がある場合MinMaxユーザーは最初に「正しい」値を増やすようにプッシュされます。そうでない場合、無効化は失敗します)。

  • 「ok」検証を使用します。

つまり、ユーザーがすべてを入力し、[OK]ボタンをクリックしたときにのみ検証できるようにするということです。

「OK」ボタンとインタラクティブなインスタント検証を組み合わせるのがユーザーにとって最適だと思います。ユーザーは入力によってどこで間違いを犯したかを知っていますが、それでも自由に閲覧でき、[OK]ボタンをクリックした後にのみ検証から「スラップ」を取得します(このステップでは、彼が行ったエラーの最初に表示するだけで、必要ありませんそれらすべてを表示します)。

エラーメッセージは、セッターが昔ながらのLastError方法で、またはのテキストとして提供できますException

于 2013-03-15T11:55:32.333 に答える