是否有验证用户输入的最佳做法?
实际问题:
用户在窗口中提供某些输入。当他完成这些输入后,他可以点击“创建”。现在,应显示一条弹出消息,其中包含所有无效输入。如果没有无效输入,则继续。
我可以在Form类中轻松完成此操作。但我记得在设置属性中验证输入的一些最佳实践方法。问题是我已经创建了该类的实例(否则,无法设置属性;))如果我以这种方式验证。这不应该发生,除非输入有效,否则不会创建类的实例。
我打算创建一个ErrorMessages类,其中包含一个列表,我可以将所有errorMessages放入其中。每次给出无效输入时,都会向errorMessages列表添加一条新消息。因此,如果用户单击“创建”按钮,则会显示列表中的所有消息。这是处理事情的好方法吗?
那么有最佳实践方法吗?任何提供此类解决方案的设计模式?
修改:这是一项学校任务。因此有不合逻辑的要求。单击“创建”时,我必须显示所有无效输入。我想从Form类中做到这一点。 (因此即使没有GUI,验证也能正常工作,此时我甚至还没有创建GUI)。首先确保我的功能正常工作;)。我想保持我的代码干净,抽象和OOP。那么我应该如何显示我的错误消息?
答案 0 :(得分:8)
我打算创建一个ErrorMessages类,其中包含一个列表,我可以将所有errorMessages放入其中。每次给出无效输入时,都会向errorMessages列表添加一条新消息。因此,如果用户单击“创建”按钮,则会显示列表中的所有消息。这是处理事情的好方法吗?
主观上,我认为提供即时反馈意味着用户输入的值无效。这样,他们可以立即返回并修复它。
我的意思是,考虑一下。你提出的方法最终会给他们一个巨大的问题列表,这对用户不是很友好。此外,他们如何记住所有这些问题,以便能够一次又一个地修复它们? (提示:他们不是。)
相反,我建议使用ErrorProvider
class来显示相应控件旁边的任何错误。我在答案here和here中谈了这个方法。
当然,您仍然需要确保最终提交(单击确定/提交按钮)所有输入都有效,但这只是检查是否存在任何错误的简单情况。
我可以在Form类中轻松完成此操作。但我记得在设置属性中验证输入的一些最佳实践方法。
是的,这里的想法是封装。 Form类应该只知道表单内容。不应该要求知道哪种输入对所有不同的控件都有效。
相反,此验证逻辑应放在其他位置,例如存储数据的类中。该类将公开属性以获取和设置数据,并且在setter方法内部,它将验证数据。
这意味着您的表单所要做的就是在数据类上调用setter方法。表单不需要知道如何验证数据,甚至不知道数据的含义,因为数据类处理所有这些。
这不应该发生,除非输入有效,否则不会创建类的实例。
如果确实如此,则需要为该类提供构造函数,该类接受所需的所有数据作为参数。然后,构造函数的主体将验证指定的数据,并在其中任何一个无效时抛出异常。该异常将阻止创建类,确保不存在包含无效数据的类的实例。
这样的类可能不会在只有getter的情况下使用setter方法。
然而,这在C#世界中是一种不寻常的要求(不管它在C ++中是多么常见)。通常,将验证代码放在setter中就可以了。
我的房产有一些私人制定者。所以它们只能在我的数据类的构造函数中设置。问题是,这似乎使我的验证不是eassy
为什么会改变什么?您仍然处理私有setter内部的验证。如果验证失败,则抛出异常。因为构造函数不处理异常,所以它继续从该方法冒出来到试图实例化该对象的代码。如果该代码想要处理异常(例如,向用户显示错误消息),则可以这样做。
当然,在无效输入的情况下抛出异常不一定是“最佳实践”。原因是异常通常应该保留用于意外情况,并且用户搞砸并向您提供无效数据也是可以预期的。但是:
答案 1 :(得分: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
}
有什么好处:
缺点:
答案 2 :(得分:2)
请参阅ErrorProvider
课程(文档here)。它提供了一组标准的可视指示器,可以附加到大多数标准的WinForms控件上。
答案 3 :(得分:0)
有几种可能的方法:
当用户输入值时,会在输入(TextChanged
)期间检查并立即验证。创建一个新类的实例,调用属性/方法应该接受string
并返回bool
(或者在属性的情况下抛出Exception
),在false
上 - 绘制特殊错误条件(文本框旁边的红色标签,闪烁的东西,ErrorProvider
或任何你应该做的事情应该告诉用户“错误!”)。
我喜欢使用这个,但有点不同,通常我只检查Type
,然后尝试直接在表单中解析它。如果表单与string
一起运行,并且所有格式和验证都发生在类(属性设置器)中,则可以抽象更多。或者您可以提供具有附加信息的表单(通过使用查询方法或属性),以便它可以进行即时验证,而无需实例化类或使用setter。例如,可以在表单(甚至控件)中标识double factor
属性,以执行'double.Parse and you can have attribute
DefaultValue which can be used to display to the user value in the different way when it's different from default (like it is done by
PropertyGrid`)。
当用户完成输入时,验证(通过尝试设置值并捕获异常),如果错误 - 用户不能“离开”或“进度”,直到他按ESC(取消更改)或更正输入以通过验证
这个我不喜欢。持有用户的想法让我烦恼(和用户ofc)。此外,很难实现交叉检查(例如,如果您有Min
和Max
值,那么将推动用户首先增加“右”,否则失效将失败。
这意味着让用户输入所有内容,只有在他点击“确定”按钮时才会验证。
我认为结合“确定”按钮和交互式即时验证对用户来说是最好的。当用户通过输入知道他在哪里犯了错误,但是仍然可以自由浏览并且只有在点击“确定”按钮后才能从验证中获得“打击”(在这一步你可以简单地向他展示他做的第一个错误,没有必要向所有人展示。)
错误消息可由设置者以旧式LastError
方式提供,也可以Exception
中的文本提供。