10

是否有验证用户输入的最佳实践方法?

实际问题:

用户在窗口中给出某些输入。当他完成这些输入后,他可以单击“创建”。现在,应该显示一条弹出消息,其中包含所有无效输入。如果没有无效输入,则继续。

我可以在 Form 类中轻松做到这一点。但我记得一些验证设置属性中输入的最佳实践方法。问题是如果我以这种方式验证,我已经创建了该类的实例(否则,无法设置属性;))。这不应该发生,除非输入有效,否则不能创建类的实例。

我打算创建一个 ErrorMessages 类,其中包含一个可以放置所有 errorMessages 的列表。每次给出无效输入时,都会将一条新消息添加到 errorMessages 列表中。因此,如果用户单击“创建”按钮,则会显示列表中的所有消息。这是处理事情的好方法吗?

那么有没有最佳实践方法?任何提供这种解决方案的设计模式?

编辑:这是学校的任务。所以有不合逻辑的要求。单击“创建”时,我必须显示所有无效输入。我想在 Form 类之外做到这一点。(因此即使没有 GUI,验证也可以工作,此时我什至还没有创建 GUI)。首先确保我的功能正常工作;)。我想保持我的代码干净、抽象和 OOP。那么我应该如何显示我的错误信息呢?

4

4 回答 4

8

我打算创建一个 ErrorMessages 类,其中包含一个可以放置所有 errorMessages 的列表。每次给出无效输入时,都会将一条新消息添加到 errorMessages 列表中。因此,如果用户单击“创建”按钮,则会显示列表中的所有消息。这是处理事情的好方法吗?

主观上,我认为最好提供用户输入的值无效的即时反馈。这样,他们可以立即返回并修复它。

我想说的是,考虑一下。你提出的方法最终会给他们一个巨大的问题列表,这不是很用户友好。此外,他们将如何记住所有这些问题以便能够一次又一次地修复它们?(提示:他们不是。)

相反,我建议使用ErrorProvider该类在适当的控件旁边显示任何错误。我在这里这里的回答中更多地谈到了这种方法。

当然,您仍然需要确保在最终提交时(单击 OK/Submit 按钮)所有输入都是有效的,但这只是检查是否存在任何错误的简单案例。

我可以在 Form 类中轻松做到这一点。但我记得一些验证设置属性中输入的最佳实践方法。

是的,这里的想法是封装。Form 类应该只知道表单的东西。不应该要求知道哪种输入对所有不同的控件都有效/无效。

相反,这个验证逻辑应该放在其他地方,例如存储数据的类中。该类将公开公共属性以获取和设置数据,并且在 setter 方法内部,它将验证数据。

这意味着您的 Form 所要做的就是在您的数据类上调用一个 setter 方法。表单不需要知道如何验证数据,甚至不需要知道数据的含义,因为数据类处理所有这些。

这不应该发生,除非输入有效,否则不能创建类的实例。

如果确实如此,您将需要为类提供一个构造函数,该构造函数接受它需要的所有数据作为参数。然后构造函数的主体将验证指定的数据并在其中任何一个无效时抛出异常。该异常将阻止创建类,确保不存在包含无效数据的类的实例。

这样的类可能根本没有 setter 方法——只有 getter。

然而,这在 C# 世界中是一种不寻常的要求(尽管它在 C++ 中可能很常见)。通常,将验证代码放在 setter 中就可以了。

我的财产有一些私人二传手。所以它们只能在我的数据类的构造函数中设置。现在的问题是,这似乎使我的验证并不容易

为什么这会改变什么?您仍然在私有 setter 内部处理验证。如果验证失败,则抛出异常。因为构造函数不处理异常,它继续从该方法冒泡到试图实例化对象的代码。如果该代码想要处理异常(例如,向用户显示错误消息),它可以这样做。

当然,在输入无效的情况下抛出异常不一定是“最佳实践”。原因是通常应该为意外情况保留异常,并且用户搞砸并向您提供无效数据是可以预料的。然而:

  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(或在属性的情况下抛出Exception),on false- 绘制特殊错误条件(文本框旁边的红色标签,闪烁的东西,ErrorProvider或者你能做什么应该告诉用户“错了!”)。

我喜欢使用这个,但有点不同,通常我只检查Type然后尝试直接在表单中解析它。如果表单使用string's 操作并且所有格式和验证都发生在类中(属性设置器),则可以抽象更多。或者您可以为表单提供附加信息(通过使用查询方法或属性),以便它可以进行即时验证,而无需实例化类或使用 setter。例如,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”验证。

这只是意味着让用户输入所有内容,并且仅在他单击“确定”按钮时进行验证。

我认为结合“确定”按钮和交互式即时验证对用户来说是最好的。因为用户知道他通过输入在哪里犯了错误,但仍然可以自由浏览,并且只有在单击“确定”按钮后才会从验证中获得“耳光”(在这一步你可以简单地向他展示他犯的第一个错误,没有必要全部展示)。

错误消息可以由 setter 以老式方式提供,也可以LastError作为Exception.

于 2013-03-15T11:55:32.333 回答