65.9K
CodeProject 正在变化。 阅读更多。
Home

委托的不同处理方式 - 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (6投票s)

2015年11月7日

CPOL

3分钟阅读

viewsIcon

11374

downloadIcon

136

使用委托来分组,处理横切关注点并创建动态验证器

引言

本系列的第 一部分中,我介绍了一些委托的基础知识,以及使用它们来分组,延迟和更改动态添加到委托列表中的函数的顺序。在第二部分中,我将演示使用委托和泛型来创建一个验证器,该验证器将验证各种类型和类。

我包含的程序是第一部分中包含的程序的扩展,并具有一个单独的窗体来演示这些新概念。

程序本身并没有做太多,但是您可以在代码中放置断点以对想法和概念进行单元测试。

程序运行时,请按标有“Part 2”的按钮以转到Form2,其中包含本文的代码示例。

背景

验证器包括两个基本类。

(Form2 区域 - 示例辅助类)

clsValidator

这是一个类,包含对名为CurrentValidatorFunc<T,bool>委托的引用,以及对两个clsValidatorsFailValidatorPassValidator的引用。

如果CurrentValidator成功,则调用PassValidator,如果为false,则调用FailValidator

使用此功能,您可以创建一个基于truefalse值遍历的节点树状结构。

它的定义如下

    class clsValidator<T>
    {
        public Func<T, bool> CurrentValidator { get; set; }
        public clsValidator<T> FailValidator { get; set; }
        public clsValidator<T> PassValidator { get; set; }

    }

DelegateValidator

它包含以下属性
  • ValueT类型可以接受任何类型
  • ValidationRules,其中包含clsValidator对象的根节点
  • 以及一个布尔函数Valid(),该函数被调用以使用Validation规则来验证Value

它的定义如下

    class DelegateValidator<T>
    {
        public T Value { get; set; }

        public clsValidator<T> ValidationRules { get; set; }

        internal bool Valid()
        {
            //Seed the recursive routine with root node
            return Valid(ValidationRules, Value);
        }
        /// <summary>
        /// This is a recursive call which traverses the clsValidator nodes.
        /// </summary>
        /// <param name="currval">Validator function</param>
        /// <param name="RecurseValue">Value</param>
        /// <returns>true or false</returns>
        private bool Valid(clsValidator<T> currval, T RecurseValue)
        {
            //invoke the Func<T,bool> to test the Value
            bool success = currval.CurrentValidator.Invoke(RecurseValue);
            if (success)
            {
                if (currval.PassValidator != null)
                    return Valid(currval.PassValidator, RecurseValue);
            }
            else
            {
                if (currval.FailValidator != null)
                    return Valid(currval.FailValidator, RecurseValue);
            }

            return success;
        }
    }

Using the Code

(Form 2 区域一,示例一)

我定义以下内容来测试整数值

        private void btnExample1_Click(object sender, EventArgs e)
        {
            //We are going to be testin integer values
            DelegateValidator<int> dint = new DelegateValidator<int>();

            bool retval;

            //Create a linked list of clsValidator, 
            //Current validator test to see if Value is greater than 1, 
            //if so then next validator (PassValidator) tests to see if Value is also < 10
            dint.ValidationRules = new clsValidator<int> { 
                CurrentValidator = (i) => { return i > 0; }, 
                PassValidator = new clsValidator<int> { 
                    CurrentValidator = (i) => { return i < 10; } 
                } 
            };

            dint.Value = -1;
            retval = dint.Valid(); //False, less then 0 - fails first validator, 
                                   //never goes to second validator

            dint.Value = 5;
            retval = dint.Valid(); //True, greater then 0 and 
                                   //less then 10 - both validators return true

            dint.Value = 50;
            retval = dint.Valid(); //False, greater then 0 and 
                                   //greater then 10 - first validator passes, second fails.
        }

这是一个图表,说明了代码中显示的内容

True Validator

请注意,您不需要为所有路径都创建验证器,只需创建您要进一步测试的路径即可。因此,当第一个验证测试失败时,我们只需从Valid()返回fail。

(Form 2 区域二,示例二)

我定义以下内容来测试string值,上半部分使用示例一中显示的内联语法,下半部分显示使用在点击事件范围之外定义的函数。

        private void btnExample2_Click(object sender, EventArgs e)
        {
            //First test - defining validator with anonymous functions
            DelegateValidator<string> delstr = new DelegateValidator<string>();
            delstr.Value = "Steve";
            //inline validation - first length > 1 then first char is Upper case
            delstr.ValidationRules = new clsValidator<string>() 
                { CurrentValidator = (s) => { return s.Trim().Length > 1; }, 
                    PassValidator = new clsValidator<string>() 
                    { CurrentValidator = (s) => { return char.IsUpper(s, 0); } } 
                };
            bool isValid = delstr.Valid(); // With "Steve", Validation passes.

            //Second Test - defining validators with functions which match the signature.
            clsValidator<string> mv = new clsValidator<string>();
            mv.CurrentValidator = LengthMinValidator; //Check to see if it passes min length
            mv.PassValidator = new clsValidator<string>(); //If so,
            mv.PassValidator.CurrentValidator = LengthMaxValidator; //Check to see if max length

            mv.PassValidator.PassValidator = new clsValidator<string>() { 
                CurrentValidator = FirstLetterUpperValidator };
            
            mv.PassValidator.FailValidator = new clsValidator<string>();
            //If max validator fails, check to see if number.
            mv.PassValidator.FailValidator.CurrentValidator = IsNumberValidator; 

            delstr.ValidationRules = mv; //Assigning new rules.
            delstr.Value = "cat";
            isValid = delstr.Valid(); //Should be false, passes min and max but not Upper case;

            delstr.Value = "10000";
            isValid = delstr.Valid(); //Should be true, long and numeric

            delstr.Value = "10";
            isValid = delstr.Valid(); //Should be false, not long and not upper case;

            delstr.Value = "Cat";
            isValid = delstr.Valid(); //Should be true;
        }

        private bool LengthMinValidator(string s)
        {
            Console.WriteLine("LengthMinValidator");
            return s.Length > 1;
        }

        private bool LengthMaxValidator(string s)
        {
            Console.WriteLine("LengthMaxValidator");
            return s.Length < 5;
        }

        private bool FirstLetterUpperValidator(string s)
        {
            Console.WriteLine("FirstLetterUpperValidator");
            return Char.IsUpper(s, 0);
        }

        private bool IsNumberValidator(string s)
        {
            Console.WriteLine("IsNumberValidator");
            long j;
            return Int64.TryParse(s, out j);
        }

(Form2 区域 - 示例辅助类)

最后一个示例显示了一个可以验证的类。它使用定义为的类

class clsEmployee
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public DateTime BirthDate { get; set; }
        public string Webpage { get; set; }

    }

为了使这一点更清楚,我将所有验证器定义为在点击事件之外的单独验证器。

我为此类创建的测试如下

  • 所有实例都必须填写FirstNameLastName
  • 如果实例包含Webpage,请使用regex进行验证。
  • 如果实例的年龄大于零,请针对输入的Birthdate进行确认。

定义以下验证函数

(Form 2 区域三,示例三)

        private bool checkFirstName(clsEmployee e)
        {
            return e.FirstName != null && e.FirstName.Length > 0;
        }
        private bool checkLastName(clsEmployee e)
        {
            return e.LastName != null && e.LastName.Length > 0;
        }

        private bool checkWebAddress(clsEmployee e)
        {
            //Regex from google search :)   
            Regex rg =
                new Regex(@"[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)");
            if (string.IsNullOrEmpty(e.Webpage))
                return true;
            if (e.Webpage.Trim().Length == 0)
                return true; // not an error
            else
                return rg.IsMatch(e.Webpage);
        }

        private bool checkAge(clsEmployee e)
        {
            if (e.Age > 0)
            {
                if (DateTime.Now.Year - e.BirthDate.Year == e.Age)
                    return true;

                return false;
            }
            else
                return true; //not an error not to enter an age
       
        }

这里有一些我添加到示例中的人员示例。前两个将验证为true,后两个将验证为false

            clsEmployee ce1 = new clsEmployee() {
                FirstName = "Bob", LastName = "Marley"
            };
            clsEmployee ce2 = new clsEmployee() {
                FirstName = "Steve",
                LastName = "Contos",
                Webpage = @"<a href="http://www.PolarisSolutions.com">http://www.PolarisSolutions.com</a>"
            };
            //will fail on both webpage and if you fix page will fail on age
            clsEmployee ce3 = new clsEmployee() {
                FirstName = "Bill",
                LastName = "Gates",
                Webpage = @"<a href="http://wwwMicrosoftcom">http://wwwMicrosoftcom</a>",
                Age = 10,
                BirthDate = Convert.ToDateTime("10/28/1955").Date
            };
	    //Will fail because no last name	
            clsEmployee ce4 = new clsEmployee()
            {
                FirstName = "Steve"
            };

最后,这是您连接验证器的方式

            DelegateValidator<clsEmployee> dv = new DelegateValidator<clsEmployee>();
            dv.ValidationRules = new clsValidator<clsEmployee>()
            {
                CurrentValidator = checkFirstName,
                PassValidator = new clsValidator<clsEmployee>()
                {
                    //First name passes
                    CurrentValidator = checkLastName,
                    PassValidator = new clsValidator<clsEmployee>()
                    {
                        //Last name passes
                        CurrentValidator = checkWebAddress,
                        PassValidator = new clsValidator<clsEmployee>()
                        {
                            //Web address passes
                            CurrentValidator = checkAge
                        }
                    }
                }
            };

同样,我只分配了PassValidator,但是您也可以分配FailValidator

关注点

我希望本文展示了解决常见问题的另一种方法。其他方法包括使用Microsoft Enterprise库,该库包含强大的验证机制。如果您喜欢这个主题,您可能还想进一步研究表达式树,该表达式树也使用委托和lambda表达式。

© . All rights reserved.