356. Validation interception

נראה הפעם עוד שימוש לInterception.

נניח שיש לנו פונקציה ואנחנו מעוניינים לבדוק האם הפרמטרים שלה תקינים.

למשל:

1
2
3
4
public int MyMethod(int id, string name)
{
// MyMethod Content
}

אופציה אחת היא להוסיף את הוואלידיציה למתודה עצמה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int MyMethod(int id, string name)
{
if (id < 0)
{
throw new ArgumentException("Argument must be positive", "id");
}
if (name == null)
{
throw new ArgumentNullException("name");
}
// MyMethod Content
}

אלא ששימו לב שעל כל ארגומנט שיש לנו בפונקציה שאנחנו רוצים לבדוק, אנחנו מוסיפים עוד 3 שורות.

כך שאם יש לנו 3 פרמטרים שאנחנו צריכים לבדוק בפונקציה, נצטרך להוסיף לה 9 שורות לפחות.

ראינו בעבר דרך להוריד את מספר השורות (טיפ מספר 292), אבל זה פתרון חלקי, כי הוא מתמודד רק עם nullים. (הוא עדיין משאיר שורה אחת של וואלידציה במתודה, אבל אפשר להגיד שזה סביר)

אז איך נפתור את זה בInterception?

פה אנחנו נכניס אלמנט שלא השתמשנו בו עד כה והוא הוספת Attributeים מעל פרמטרים של הפונקציה (ראו גם טיפ מספר 245)

יהיה לנו Attribute שיציין שאנחנו מעוניינים בוואלידציה ומהסוג שלה, ונשים אותו מעל פרמטרים.

להלן דוגמה לAttribute כזה:

1
2
3
4
5
[AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
public abstract class ValidationAttribute : Attribute
{
public abstract bool IsValid(object obj);
}

והנה שני מימושים לדוגמה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NotNullAttribute : ValidationAttribute
{
public override bool IsValid(object obj)
{
return (obj != null);
}
}
public class IsPositiveAttribute : ValidationAttribute
{
public override bool IsValid(object obj)
{
int? value = obj as int?;
return (value >= 0);
}
}

כעת הInterception שלנו יעשו משהו פשוט 😃:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void Route(MethodCallInfo call)
{
var parameters =
call.Method.GetParameters().
Select((x, i) => new {Parameter = x, Index = i})
.Where(x => x.Parameter.GetCustomAttributes(true)
.OfType<ValidationAttribute>().Any());
foreach (var parameter in parameters)
{
ValidationAttribute attribute =
parameter.Parameter.GetCustomAttributes(true)
.OfType<ValidationAttribute>().First();
if (!attribute.IsValid(call.Arguments[parameter.Index]))
{
throw new ArgumentException("Argument wasn't valid",
parameter.Parameter.Name);
}
}
call.Proceed();
}

שימו לב שאנחנו תמיד שמים אותה הודעה בException, אבל אפשר לעשות שזה גם יגיע מהAttribute.

אפשר גם שהAttribute יקבל את השם של הפרמטר והוא יזרוק את הException.

כעת בהנחה ויש לנו Interception כזה, פשוט נשים Attributeים מתאימים:

1
2
3
4
public int MyMethod([IsPositive]int id, [NotNull]string name)
{
// MyMethod Content
}

מגניב ביותר!

בהמשך כשנראה דרכים יותר יפות לממש Interception, יהיה גם כיף להשתמש בדברים כאלה.

סופ"ש ואלידי טוב.

שתף