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






4.68/5 (6投票s)
使用委托来分组,处理横切关注点并创建动态验证器
引言
在本系列的第 一部分中,我介绍了一些委托的基础知识,以及使用它们来分组,延迟和更改动态添加到委托列表中的函数的顺序。在第二部分中,我将演示使用委托和泛型来创建一个验证器,该验证器将验证各种类型和类。
我包含的程序是第一部分中包含的程序的扩展,并具有一个单独的窗体来演示这些新概念。
程序本身并没有做太多,但是您可以在代码中放置断点以对想法和概念进行单元测试。
程序运行时,请按标有“Part 2”的按钮以转到Form2
,其中包含本文的代码示例。
背景
验证器包括两个基本类。
(Form2 区域 - 示例辅助类)
clsValidator
这是一个类,包含对名为CurrentValidator
的Func<T,bool>
委托的引用,以及对两个clsValidators
,FailValidator
和PassValidator
的引用。
如果CurrentValidator
成功,则调用PassValidator
,如果为false
,则调用FailValidator
。
使用此功能,您可以创建一个基于true
或false
值遍历的节点树状结构。
它的定义如下
class clsValidator<T>
{
public Func<T, bool> CurrentValidator { get; set; }
public clsValidator<T> FailValidator { get; set; }
public clsValidator<T> PassValidator { get; set; }
}
DelegateValidator
Value
–T
类型可以接受任何类型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.
}
这是一个图表,说明了代码中显示的内容
请注意,您不需要为所有路径都创建验证器,只需创建您要进一步测试的路径即可。因此,当第一个验证测试失败时,我们只需从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; }
}
为了使这一点更清楚,我将所有验证器定义为在点击事件之外的单独验证器。
我为此类创建的测试如下
- 所有实例都必须填写
FirstName
和LastName
。 - 如果实例包含
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表达式。