繁体   English   中英

C# - 执行验证的位置

[英]C# - Where to perform validation

我有一个关于何时验证C#表单应用程序输入的问题。

从表单应用程序获取输入时,您可以在解析主表单类中的文本字段中的数据时运行所有验证,然后在任何地方使用。 例如:

方法1:

class Car
{
    private string registration { set; get; } // NO VALIDATION HERE
}
// in the form class
private void add_Click(object sender, EventArgs e)
{
    // get registration
    int reg_valid = Validate.registration(txtReg.Text); // VALIDATION IS DONE HERE

    if(reg_valid)
    {
        Car.registration = txtReg.Text;
    } else {
        // Registration invalid - throw error
    }
}

实际的Car对象中没有验证,因为它都是在表单类中完成的。

另一种方法是验证存储在其中的实际类中的输入,如:


方法2:

class Car
{
    // out registration
    private string registration;

    // we can set our registration here and return false if there is an error
    bool set_registration(registration)
    {
        int reg_valid = Validate.registration(registration); // VALIDATION IS DONE HERE

        if(reg_valid) {
            this.registration = registration;
        } else {
            return false; // if error
        }

        return true; // if all goes well
    }
}
// in the form class
private void add_Click(object sender, EventArgs e)
{
    // get registration
    string registration = txtReg.Text; // NO VALIDATION DONE HERE

    // we can then store this in our object
    if( ! Car.set_registration(registration))
    {
        // Registration invalid - throw error
    }
}

方法1似乎更清晰,但我们使用私有属性的原因之一是我们可以验证任何解析参数,这就是方法2似乎更好的选择。 此外,使用方法2,错误必须在堆栈中可能多次执行,这导致重复的if语句。

*请注意,此代码尚未运行,因此可能包含错误。

您应该拥有类的核心以及与用户界面相关的那些类。 首先,创建应用程序的Core类( 业务逻辑 )。 当然,你应该在这里做验证。 实际上,代码就像你不知道谁将实现用户界面一样。 此外,考虑到用户界面可以更改,而核心类应保持不变。

在对用户界面进行编码时,您将意识到如果您不进行任何验证,则在用户输入某些数据案例时会引发异常。 这将使您在用户界面类中也创建验证。

所以,答案是,您最终将对应用程序的两个部分进行验证。

警告:基于意见的答案!

这取决于项目的要求规模时间 (以及许多其他重要因素,如“用户友好应用程序”或不重要,如“我自己的品味会......”

由于大范围的每个个体可能的情况下,答案不能在一般的准则 ,其中满足每个人的需求表达

您的问题解决了两个要操纵的对象:

  • 一个用作用户界面的表单,用户可以在其中输入任何内容(假设由“用户输入*任意*值然后单击按钮”定义
  • 一个Class属性,当它有效时,它被赋值(Setter)。

  • 没有时间 ,需求只是告诉“输入一个值”,这是Project中的一个小任务对于这个Button中使用的小类没有至关重要的意义,而且你很懒惰:

    =>在按钮单击中,使用一个或两个禁止检查来进行严格验证,这些检查会删除任何非允许值,并根据错误的性质显示MessageBox。 只有将值传递给类,并在有有效值时更进一步。

  • 没有时间 ,简单的要求,没有关键重要性的小任务 ,但是主要使用了一个 ,但你很懒惰:

    => 在类中移动上面的验证,然后使用自定义消息抛出一个,两个或三个常规异常。 在使用该类的任何UI操作中,使用Try / Catch捕获ButtonClick中的异常,并在捕获的异常情况下显示MessageBox而不进一步。

  • 您有5分钟 ,简单的要求, 小的非关键任务, 重要的类角色:

    =>像以前一样,但是,决定是否更推荐另一种显示用户的方式(取决于您的目标用户),例如在单击按钮后更改TextBox的背景颜色,在Label中显示消息,而不是让用户烦恼一个对话框......

  • 你有10分钟 ,简单的要求, 重复的任务重要的类角色

    =>找到一种快速测试给定值的方法,并在类中创建一个公共或朋友(静态?)函数该函数返回验证尝试的结果,如可能的验证错误的UICarValidationEnum枚举(*参见下面的编辑):
    a) UICarValidationEnum.Valid
    b) .TooLong
    c) .NullValue
    d)。 .ForbiddenChars
    e)......
    然后,您可以在类的内部和外部使用该验证功能。 这样,您可以在执行Setter之前和执行Setter 时同时处理无效的Setter尝试,以及使用您的类以及UI不使用 缺点是,如果检查数据在类外是否有效,则无法避免双重验证(例如,在按钮中)

  • 时间的问题 ,但要求是高效的投入 ,非常重要的重复性任务,类大量使用 -你不能偷懒:

    =>处理UI验证和类验证。 为什么? UI部分解决“高效输入”部分,而类部分解决“类的角色”。 您可以使用上面的验证器函数,或者在上面的其他情况下在Setter上实现更多的异常。 通过在按钮单击时向用户提供更多信息,您增加了处理的异常/无效输入的数量。
    记住:类很常用。 这里的举措是实现最大化编码以处理无效输入的不同情况, 以减少在项目中的其他位置写入的代码量,同时仍然能够处理所有这些情况。

  • 时间很重要用户友好的应用程序, 重复的任务, 重要的课程:

    =>重新考虑UI布局和行为,验证主要是UI,但Class仍然自己做:
    a)用户讨厌对话框(无论是DialogBox,消息,openfile等等......尽可能避免使用它们)
    b)用户讨厌侵略性的颜色
    c)用户讨厌验证
    d)......用户讨厌很多东西..!

    上面的函数验证的作用非常重要:捕获用户输入操作。 例如,如果它是TextBox,则捕获TextChanged事件,然后调用类验证函数。 更新标签中的信息,通知任何遇到的错误,并将TextBox的背景颜色更改为友好的颜色,但与错误标准相关,如浅粉红色。 不要使用Color.Red,这对大多数用户来说过于激进。 每天看200次红色可能会在一天结束时导致意外行为。
    仅在所有输入有效时启用按钮。 不要为每个输入创建一个按钮,用户讨厌无休止的验证。

  • 时间并不重要用户友好的应用程序, 重复的任务, 重要的类:

    =>与之前的选项一起,改进UI响应。
    在标签中添加图形图标,和/或考虑在表单中使用ErrorProvider。 用户喜欢简单的图标,而不是类似的长消息。
    考虑使用友好的声音来通知弹出错误(不是每个人都是“视觉接受”

  • 时间无关紧要用户友好的应用程序, 重复的任务, 重要的类:

    =>继续实时捕获用户输入,但实施与之相关的暗示性更正。 如果它是TextBox,当用户尝试输入无效数据时,请在集合中使用带有预定义和固定输入建议的AutoCompleteCollection(您需要一个函数)。
    当与用户的选择/操作不兼容时,实时禁用其他输入。

  • 时间真的很重要用户友好推荐, 重复性任务, 重要的课程:

    =>不要使用验证例程重载表单
    为该特定输入创建Custom Control 使用验证重载您的自定义控件 如果您的类无法在此UI之外使用,请不要在内部创建验证。 移动自定义控件中的所有内容,甚至禁止无效字符(如果它是文本框)。 在这种特定情况下, 您将使用UserControl作为Datas的Validator组件。
    否则,在输入时使用类验证功能方案,并尽可能通过该用户控件以用户友好的方式显示相应的错误(在没有UserControl的情况下可以使用您的类的情况,通常是这种情况)


等等

我知道我更倾向于考虑使用应用程序的用户而不是要编写代码的用户。 但看看Baltasarq的答案 事实上,最好的举措就是两者兼而有之。 这三个第一选项只适用于整个项目中没有真正重要性的简单类。 我们都开始创建那个小类,我们没有打算做出强有力的验证控制。 我认为我们大多数人仍然使用这类课程。 然而,随着时间的推移, 其中一些类的使用越来越多。然后我们面临着更强的验证方案的需要。

那是偷工减料的方式 这是不安全的,但它有效......直到发生一些事情......

=>尽可能尝试两者兼顾。 你不知道会发生什么,谁会使用你的班级,你的老板是否会改变主意并要求你为该班级创建一个特定的用户界面,以便每个人都能使用...

有一段时间,当我有时间创建一个类时,我实际上创建了至少两个类:

- MyClass_Class
- MyClass_Form
- (也许是MyClass_UserControl
- (也许是MyChildClass WithUnsafeSetters - 用于设置已经验证了性能需求的值)

核心类始终提供实时验证功能......


编辑:

使用枚举的属性验证器函数示例...

[Flags]
public enum registrationValidation_Enum
{
    Valid = 0x01,
    TooLong = 0x02,
    InvalidChars = 0x04,
    NullEntry = 0x08
    // ...
}

这个枚举可以封装在类中。 枚举比异常类更容易处理/记忆/检索。

这是Property Getter / Setter。

class Car
{
    private string registration = "Unknown";

    public string Registration
    {
        get
        {
            return registration;
        }
        set
        {
            validate_registration(value, True);
            // Setter for the Property.
            // Throws an Exception upon invalid value.
        }
    }
}

这是一个公共的Validator函数:

    public registrationValidation_Enum test_registration(
        string newRegistration)
    {
        registrationValidation_Enum checkResult =
            registrationValidation_Enum.Valid;

        // Do the checks here
        if (newRegistration.Length > 10)
        {
            checkResult = checkResult | registrationValidation_Enum.TooLong;
        }
        if (containsNonAlphNumericChars(newRegistration))
        {
            checkResult = checkResult | registrationValidation_Enum.InvalidChars;
        }
        // ...
        return checkResult;
    }

这是塞特的公共版本:

    // this bypass the double check : attempts to set the value if Valid.
    // otherwise, either returns a validation result,
    // either throws an exception.
    public registrationValidation_Enum validate_registration(
        string newRegistration,
        bool canThrowException)
    {
        bool isValid = test_registration(newRegistration);
        if (isValid == registrationValidation_Enum.Valid)
        {
            registration = newRegistration;
            return registrationValidation_Enum.Valid;
        }
        else
        {
            if (canThrowException)
            {
                string exceptionMessage = "";
                if (isValid | registrationValidation_Enum.TooLong)
                {
                    exceptionMessage += "Registration too long" 
                                     + Environment.NewLine;
                }
                if (isValid | registrationValidation_Enum.InvalidChars)
                {
                    exceptionMessage += 
                        "Registration contains invalid characters"
                        + Environment.NewLine;
                }
                // ....
                Throw New Exception(exceptionMessage);
            }
            else
            {
                return isValid;
            }
        }
    }

公共 validate_registration(string, false)有以下情况:

  • 你不想在Button Click上加倍验证
  • 你没有监控用户输入
  • 你不想在任何地方处理级联异常
  • 你想要控制每个UI上下文的自定义错误消息(你不能总是有一个标签来写错误)
  • 在尝试设置注册值时,单击按钮就连续排列。

将验证结果放在任何UI端的变量中,并根据您可以显示的UI组件显示相应的通知/用户选择...这对于Exceptions来说不是那么简单:想象一下,您同时拥有TooLong和InvalidChars。 你打算显示一个“Too Long”对话框,然后点击按钮,然后显示另一个“Invalid Chars”对话框?

注意:要使用Culture创建类Localizable ,使用自定义Exception消息,我将定义类级别消息(字符串)变量,其值取决于加载的Culture。

您可以将公共属性与私有成员一起使用,您可以在其中对属性进行检查,然后将其分配给类成员。

class Car
{
    private string registration;

    public string Registration 
    {
        get { return registration;}
        set { 
                if(Validate.registration(value))
                    registration = value;
                else
                    throw new Exception("Your exception message here");
            }

    }
}

private void add_Click(object sender, EventArgs e)
{
    Car.Registration = txtReg.Text;
}

有2个验证:

  • 数据类型/属性限制/解析;
  • 逻辑(属性之间的值);

看看PropertyGrid 如果您的属性接受值或抛出,那么基本(并且非常充分)验证是:

class Car
{
    private int _someValue;
    public int SomeValue
    {
        get { return _someValue; }
        set
        {
            if(value > 100)
                throw new OutOfRangeException(...);
            _someValue = value;
        }
    }
}

这确保了Car可以验证自己的属性,无论它们如何设置(反序列化, PropertyGrid ,直接属性值,反射等)。

另一件事是一些逻辑验证,它不能在实例级别执行,或者你根本不关心它(让实例创建没有例外)。 这个必须放入编辑器代码(形成用于创建实例的内容)。

至于我自己,谈论TextBox ,我更喜欢自定义MyTextBox控件,它具有所有解析相关的验证(方法GetDouble()GetInt()GetIntTime()等)以及设置/查询它的可能性(属性IsDoubleIsInt ,等等。)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM