C# - 执行验证的位置

时间:2014-12-01 09:37:07

标签: c# validation oop object stack

我有一个关于何时验证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语句。

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

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

  

警告:基于意见的回答!

这取决于项目的要求比例,以及时间(以及许多其他重要因素,如< em>&#34;用户友好的应用程序&#34; 或者不重要,比如&#34;我自己的品味将是......&#34;

由于每个人可能出现范围广泛,答案无法在一般指南中表达,这将满足每个人的需求

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

  • 一个用作用户界面的表单,用户可以输入任何内容(假设由&#34定义;用户输入 * any * 值然后单击一个按钮&#34;
  • 一个Class属性,当它有效时,它会被赋值(Setter)。

  • 您有没时间,要求只是告诉&#34;输入一个值&#34;,这是一个小任务在项目中没有关键对于此Button中使用的小班级的重要性,你非常懒惰:

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

  • 没有时间,简单要求,小任务并不重要,但类主要使用,但您和#39;懒惰:

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

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

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

  • 您有 10分钟,简单要求,重复性任务重要班级角色

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

  • 时间很重要,但要求是高效输入重复非常重要的任务,等级 强> - 你不能懒惰:

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

  • 时间很重要用户友好应用,重复性任务,重要类:

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

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

  • 时间并不重要用户友好应用,重复任务,重要班级:

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

  • 时间根本不重要用户友好应用,重复任务,重要等级:

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

  • 时间非常重要用户友好推荐,重复任务,重要类:

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

    < / LI>

等。等等。

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

偷工减料方式。它不安全,但它有效......直到某事发生......

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

  

有一段时间,当我有时间创建一个班级时,我实际上就是这样  创建至少两个类:

   - 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.
        }
    }
}

这是一个公共验证函数:

    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;
    }

以下是Setter的公开版本:

    // 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)有以下情况:

  • 您不想在按钮单击
  • 上加倍验证
  • 您不会监控用户输入
  • 您不想在任何地方处理级联例外
  • 您希望控制每个UI上下文的自定义错误消息(您无法始终使用标签来写入错误)
  • 在尝试设置注册值时,只需点击一下按钮就行。

将验证结果放在任何UI端的变量中,并根据您可以显示的UI组件显示相应的通知/用户选择...这不会像Exceptions那样简单:想象一下,你有TooLong和InvalidChars。你打算展示一个对话框&#34; Too Long&#34;然后单击按钮,然后显示另一个&#34;无效的字符&#34;对话?

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

答案 2 :(得分:0)

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

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;
}

答案 3 :(得分:0)

有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(),{{1设置/查询它的可能性(属性GetInt()GetIntTime()等)。