270. MethodSignatureComparer

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

ראינו דרך אחת ע”י השוואת הToString()ים של המתודות, אבל זו דרך קצת דוחה.

מצאתי את המחלקה הזאת מCastle.Core (שמכיל את הפרויקט DynamicProxy של Castle), שמתיימרת להשוואות שתי חתימות של שני MethodInfoים

(יש המשך אחרי הקוד)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright 2004-2011 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
public class MethodSignatureComparer : IEqualityComparer<MethodInfo>
{
public static readonly MethodSignatureComparer Instance = new MethodSignatureComparer();
public bool Equals(MethodInfo x, MethodInfo y)
{
if (x == null && y == null)
{
return true;
}
if (x == null || y == null)
{
return false;
}
return EqualNames(x, y) &&
EqualGenericParameters(x, y) &&
EqualSignatureTypes(x.ReturnType, y.ReturnType) &&
EqualParameters(x, y);
}
private bool EqualNames(MethodInfo x, MethodInfo y)
{
return x.Name == y.Name;
}
public bool EqualGenericParameters(MethodInfo x, MethodInfo y)
{
if (x.IsGenericMethod != y.IsGenericMethod)
{
return false;
}
if (x.IsGenericMethod)
{
Type[] xArgs = x.GetGenericArguments();
Type[] yArgs = y.GetGenericArguments();
if (xArgs.Length != yArgs.Length)
{
return false;
}
for (int i = 0; i < xArgs.Length; ++i)
{
if (xArgs[i].IsGenericParameter != yArgs[i].IsGenericParameter)
{
return false;
}
if (!xArgs[i].IsGenericParameter && !xArgs[i].Equals(yArgs[i]))
{
return false;
}
}
}
return true;
}
public bool EqualParameters(MethodInfo x, MethodInfo y)
{
ParameterInfo[] xArgs = x.GetParameters();
ParameterInfo[] yArgs = y.GetParameters();
if (xArgs.Length != yArgs.Length)
{
return false;
}
for (int i = 0; i < xArgs.Length; ++i)
{
if (!EqualSignatureTypes(xArgs[i].ParameterType, yArgs[i].ParameterType))
{
return false;
}
}
return true;
}
public bool EqualSignatureTypes(Type x, Type y)
{
if (x.IsGenericParameter != y.IsGenericParameter)
{
return false;
}
if (x.IsGenericParameter)
{
if (x.GenericParameterPosition != y.GenericParameterPosition)
{
return false;
}
}
else
{
if (!x.Equals(y))
{
return false;
}
}
return true;
}
public int GetHashCode(MethodInfo obj)
{
return obj.Name.GetHashCode() ^ obj.GetParameters().Length;
// everything else would be too cumbersome
}
}

הקוד הוא קצת ארוך, אבל גם מעניין. שימו לב שהשוואה כאן היא טיפה יותר strict – משווים את המתודות השמות, ערכי ההחזר, סוגי הפרמטרים והפרמטרים הגנריים. זה לא מושלם, למשל השוואה של שתי מתודות גנריות שונות המקבלות IEnumerable<T> לא תחזיר אמת, אבל זה נראה כמו בסיס די טוב. (הייתי כותב EqualityComparer של Type שמשווה באופן דומה Typeים, ומשתמש בו במקום הפונקציה EqualSignatureTypes)

המשך יום חתימות מעולה

שתף

269. ToStringComparer

קרה לי לא פעם שהייתי צריך להשוות שני MethodInfoים, או לחלופין שני Typeים.

הדבר נשמע פשוט, ונראה שאפשר להשתמש פשוט בEquals.

אלא שיש כאן טריק. תסתכלו על הקוד הבא:

1
2
3
4
5
6
7
public class GenericClass<T>
{
public void DoSomethingGeneric(string notAGenericString)
{
// ...
}
}

עכשיו נניח שיש לנו את המחלקה הבאה:

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericProvider
{
public GenericClass<T> ProvideSomethingGeneric<T>()
{
return null;
}
public GenericClass<T> ProvideAnotherGenericThing<T>()
{
return null;
}
}

עכשיו נניח שאנחנו קוראים לקוד הבא:

1
2
3
4
5
6
7
8
9
10
Type genericProviderType = typeof (GenericProvider);
MethodInfo provideSomethingGeneric =
genericProviderType.GetMethod("ProvideSomethingGeneric");
MethodInfo provideAnotherGenericThing =
genericProviderType.GetMethod("ProvideAnotherGenericThing");
Type firstGenericType = provideSomethingGeneric.ReturnType;
Type secondGenericType = provideAnotherGenericThing.ReturnType;

למרבה ההפתעה אם נקרא לקוד הבא לא נכנס לתנאי:

1
2
3
4
if (firstGenericType.Equals(secondGenericType))
{
// False
}

לפני שאתם מחליטים לפרוש מC# ומעולם התוכנה, ננסה להסביר למה זה קורה.

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

למה? הסיבה היא כי הם נוצרים ע"י פרמטרים גנריים שונים: הראשון מתקבל ע"י שליחת פרמטר גנרי לפונקציה ProvideSomethingGeneric, ואילו השני מתקבל ע"י שליחת פרמטר גנרי לפונקציה ProvideAnotherGenericThing. אבל אלה שני פרמטרים גנריים שונים!


בפרט, אם ננסה להשוואות מתודות של הטיפוסים, גם לא נקבל שוויון:

1
2
3
4
5
6
7
8
9
10
MethodInfo firstGenericTypeMethod =
firstGenericType.GetMethod("DoSomethingGeneric");
MethodInfo secondGenericTypeMethod =
secondGenericType.GetMethod("DoSomethingGeneric");
if (firstGenericTypeMethod.Equals(secondGenericTypeMethod))
{
// False
}

הדבר הזה יכול להפריע לנו להשוות מתודות.

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


איך אפשר לפתור את זה?

בעצם מעניין אותנו אם לשתי המתודות יש אותה החתימה.

הברקה מסוימת (שהיא גם Patch) היא שהToString של MethodInfo מחזיר משהו… שדומה לחתימה של המתודה!

לכן אפשר לנצל עובדה זו. ניצור IEqualityComparer שמשווה אובייקטים לפי הToStringים שלהם:

1
2
3
4
5
6
7
8
9
10
11
12
public class ToStringComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return x.ToString() == y.ToString();
}
public int GetHashCode(T obj)
{
returnobj.ToString().GetHashCode();
}
}

כעת נוכל להשוואות מתודות וטיפוסים כך:

1
2
3
4
5
6
7
8
9
10
11
12
13
IEqualityComparer<MethodInfo> methodComparer = new ToStringComparer<MethodInfo>();
if (methodComparer.Equals(firstGenericTypeMethod, secondGenericTypeMethod))
{
// True
}
IEqualityComparer<Type> typeComparer = new ToStringComparer<Type>();
if (typeComparer.Equals(firstGenericType, secondGenericType))
{
// True
}

הדבר יכול מאוד לעזור לנו בכל המקומות בFramework המקבלים IEqualityComparer. (ראו גם טיפים מספר 111-116)

צום קל וגמר חתימה טובה!

שתף

268. StructuralComparisons StructuralComparer

בהמשך לפעמים הקודמות, קיים Out of the box בFramework הProperty הסטטי בשם StructuralComparisons.StructuralComparer.

בדומה לStructuralComparisons.StructuralEqualityComparer, הProperty הזה מאפשר לנו להשוואות שני IStructuralComparable ע”י קריאה לCompareTo שלהם והעברת עצמו:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
internal class StructuralComparer : IComparer
{
public int Compare(object x,object y)
{
if (x == null)
{
if (y != null)
{
return -1;
}
else
{
return 0;
}
}
else
{
if (y == null)
{
return 1;
}
IStructuralComparable structuralComparable = x as IStructuralComparable;
if (structuralComparable != null)
{
return structuralComparable.CompareTo(y, this);
}
else
{
return Comparer.Default.Compare(x, y);
}
}
}
}

שוב, הדבר טוב אם אנחנו מרוצים מההשוואה הרגילה של הטיפוסים שלנו (המתבצעת באמצעות השורה Comparer.Default.Compare(x, y);, ע"י קריאה לCompareTo של IComparable, במידה והטיפוסים שלנו מממשים אותו).

במידה ואנחנו לא מרוצים מההשוואה הרגילה של הטיפוסים שלנו, נצטרך לכתוב Comparer מיוחד היודע לקבל Comparer איתו הוא משווה את האיברים (במקום קריאה לComparer.Default.Compare(x, y);, נקרא לComparer אותו קיבל הComparer שלנו)

המשך יום בר השוואה טוב!

שתף

267. IStructuralComparable interface

בדומה לפעמים הקודמות, קיימים לנו בFramework הממשקים IComparable וIComparer המאפשרים לנו להשוואות Instanceים של אובייקטים.

(ראו גם טיפים מספר 117-125)

גם כאן, לפעמים היינו רוצים להשוואות מבנים מורכבים מסוימים בעזרת איזשהו Comparer:

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

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

גם כאן, נוסף לנו בFramework 4.0 ממשק בשםIStructuralComparable המאפשר לנו להשוות אובייקט מורכב לפי האובייקטים הפנימיים שלו בעזרתComparer.

הממשק נראה כך:

1
2
3
4
public interface IStructuralComparable
{
int CompareTo(object other, IComparer comparer);
}

גם פה, מערכים וTupleים מממשים אותו Explicitly (ראו גם טיפ מספר 82):

לדוגמה, נניח שיש לנו מערך של מערכים ואנחנו מעוניינים להשוות את המערכים הפנימיים לפי המקסימום שלהם:

נכתוב Comparer רגיל:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ArrayMaxComparer : Comparer<int[]>
{
public override int Compare(int[] x, int[] y)
{
int firstMax = x.Max();
int secondMax = y.Max();
if (firstMax > secondMax)
{
return 1;
}
else if (firstMax < secondMax)
{
return -1;
}
return 0;
}
}

ואז נוכל להשתמש בו בשביל להשוות שני מערכים באותו גודל של מערכים:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int[][] numbers =
new[]
{
new[] {1},
new[] {1, 2, 3},
new[] {5, 8}
};
int[][] otherNumbers =
new[]
{
new[] {1, 3, 3, 1},
new[] {1, 4, 6, 4, 1},
new[] {1, 5, 10, 10, 5, 1}
};
IStructuralComparable numbersStructuralComparable = numbers;
int result =
numbersStructuralComparable.CompareTo(otherNumbers, new ArrayMaxComparer()); // -1

קצת לא ברור איך הדבר הזה ממומש עבור מערכים, ולכן דורש הסבר:

מתבצעת השוואה לקסיקוגרפית על המערכים לפי הComparer שמועבר. מה זאת אומרת?

תחשבו על זה כמו השוואה במילון – רצים על כל האיברים של המערך הראשון והשני במקביל. משווים כל שני איברים עד שמוצאים שני איברים שהם לא שווים מבחינת הComparer. מחזירים את התוצאה של הCompareTo של שני האיברים הנ"ל (האיבר המתאים מהמערך הראשון והאיבר המתאים מהמערך השני)

גם עבור Tuple הדבר מומש בצורה טובה – משווים רכיב רכיב עד שמוצאים רכיב עבורו לא מתקיים שהCompareTo בין הרכיבים של שני הInstanceים הוא 0. ברגע שמוצאים כזה, מחזירים את התוצאה.

זה מתאים למקרים בהם אנחנו מעוניינים להשוות את הטיפוסים שלנו לפי שדות, כאשר יש עדיפות לשדות, למשל קודם לפי שם פרטי, לאחר מכן לפי שם משפחה, אחר כך לפי גיל וכו’…

המשך יום מובנה בר השוואה טוב.

שתף

266. StructuralComparisons StructuralEqualityComparer

אז ראינו בפעם שעברה שאפשר להשוואות מערכים או Tupleים באמצעות הממשק IStructuralEquatable.

היינו רוצים להשוואות לפעמים מסיבות כאלה ואחרות באמצעותEqualityComparer מתאים. (כי הFramework תומך בזה בהרבה מקומות)

למרבה המזל, קיים כזה בFramework:

1
StructuralComparisons.StructuralEqualityComparer

הוא EqualityComparer סטטי המשווה שניIStructuralEquatable.

למשל:

1
2
3
4
5
6
7
int[] theNumbers = new int[] {4, 8, 15, 16, 23, 42};
int[] evil = new [] {4, 8, 15, 16, 23, 42};
if (StructuralComparisons.StructuralEqualityComparer.Equals(theNumbers, evil))
{
// True
}

יש פה משהו קצת מוזר: איפה הEqualityComparer שדיברנו עליו פעם קודמת?

למרבה הצער, אנחנו לא יכולים לציין עם איזה EqualityComparer תתבצע השוואת האיברים, ולכן היא מתבצעת עם… StructuralComparisons.StructuralEqualityComparer עצמו:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[Serializable]
internal class StructuralEqualityComparer : IEqualityComparer
{
public bool Equals(object x,object y)
{
if (x != null)
{
IStructuralEquatable structuralEquatable = x as IStructuralEquatable;
if (structuralEquatable != null)
{
return structuralEquatable.Equals(y, (IEqualityComparer) this);
}
if (y != null)
{
return x.Equals(y);
}
else
{
return false;
}
}
else if (y != null)
{
return false;
}
else
{
return true;
}
}
public int GetHashCode(objectobj)
{
if (obj == null)
{
return 0;
}
IStructuralEquatable structuralEquatable = obj as IStructuralEquatable;
if (structuralEquatable !=null)
{
return structuralEquatable.GetHashCode((IEqualityComparer)this);
}
else
{
return obj.GetHashCode();
}
}
}

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

מה שכן, אפשר ללמוד מהמחלקה הזאת איך לממש EqualityComparer של IStructuralEquatable, שכן מקבל EqualityComparer מבחוץ…. (למרות שזה לא מסובך!).

המשך יום מורכב טוב!

שתף

265. IStructuralEquatable interface

הכרנו בעבר את הממשק IEqualityComparerהמאפשר לנו לציין כיצד להשוות שני איברים מטיפוס כלשהו. (ראו גם טיפים מספר 76-80, 111-116)

כפי שבוודאי חלקנו התאכזב לגלות, יש מספר בעיות עם הממשק הזה. למשל, נניח שאנחנו מעוניינים להשוות שני מערכים – היינו רוצים לבדוק כל איבר במקום מסוים במערך הראשון שווה לאיבר באותו המקום במערך השני, לפי איזשהוIEqualityComparer.

הרי שוויון מערכים הוא לפי Referenceים:

1
2
3
4
5
6
int[] theNumbers = new int[] {4, 8, 15, 16, 23, 42};
int[] evil = new int[] {4, 8, 15, 16, 23, 42};
if (theNumbers.Equals(evil))
{
// False
}

אלא שקצת מסורבל לעשות זאת.

בFramework 4.0 החליטו להוסיף ממשק בשם IStructuralEquatable המפשט את הפתרון של בעיות אלה. הממשק הוא ממשק המציין שאפשר להשוות את האובייקט שלנו עם Instance אחר, לפי המבנה הפנימי.

למה הכוונה? אם נזכר למשל בטיפ מספר 77, מתקיים ששוויון Equals של Value Types, מתקיים אם הStructים שווים רכיב רכיב, כלומר שכל השדות של הInstance הראשון שווים לכל השדות של הInstance השני.

כאמור למעלה, היינו רוצים שתהיה לנו אופציה להשוות גם, למשל, מערכים בשיטה זו – רכיב-רכיב. הממשק הזה מציין שהטיפוס שלנו תומך בהשוואת רכיב-רכיב כלשהי.

איך זה נראה?

הממשק נראה כך:

1
2
3
4
5
public interface IStructuralEquatable
{
bool Equals(object other, IEqualityComparer comparer);
int GetHashCode(IEqualityComparer comparer);
}

שימו לב שאין פה פרמטרים גנריים בניגוד לגרסה הגנרית שלIEqualityComparer אותה זכינו לפגוש, נסביר למה בהמשך.

מה שאנחנו אמורים לעשות זה להשוות את הInstance של האובייקט שלנו עם Instance אחר לפי הComparer שקיבלנו.

למשל, במערכים היינו מממשים משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public bool Equals(object other, IEqualityComparer comparer)
{
Array otherArray = other asArray;
if (otherArray == null)
{
return false;
}
else if (this.Length != otherArray.Length)
{
return false;
}
else
{
for (int i = 0; i <this.Length; i++)
{
if (!comparer.Equals(this[i], otherArray[i]))
{
return false;
}
}
return true;
}
}

לValue Types היינו מצפים למימוש כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public struct MyStruct : IStructuralEquatable
{
private int mNumber;
private string mName;
public bool Equals(object other, IEqualityComparer comparer)
{
if (!(other is MyStruct))
{
return false;
}
MyStruct otherStruct = (MyStruct) other;
if (!comparer.Equals(mNumber, otherStruct.mNumber))
{
return false;
}
if (!comparer.Equals(mName, otherStruct.mName))
{
return false;
}
return true;
}
}

אם תהיתם עד עכשיו למה הIEqualityComparer הוא לא גנרי, עכשיו קיבלנו סיבה: הסיבה היא שאנחנו אמורים להשתמש בו כדי להשוות את כל האיברים הפנימיים!

מה בנוגע לGetHashCode? זה יותר מסובך, אבל מימוש מקובל הוא לבצע XOR על הGetHashCodeים של כל האיברים הפנימיים עם הComparer, ולהחזיר את התוצאה.

למרבה השמחה, יש מספר טיפוסים בFramework שמממשים ממשק זה. ביניהם: מערכים וTupleים (ראו גם טיפ מספר 126). לValue Types דווקא אין מימוש דיפולטי לממשק זה.

המימוש אצל מערכים וTupleים הוא Explicit (ראו גם טיפ מספר 82):

1
2
3
4
5
6
7
8
9
int[] theNumbers = new int[] { 4, 8, 15, 16, 23, 42 };
int[] evil = new int[] { 4, 8, 15, 16, 23, 42 };
IStructuralEquatable theNumbersStructuralEquatable = theNumbers;
if (theNumbersStructuralEquatable.Equals( evil,EqualityComparer<int>.Default))
{
// True
}

ואפשר לדחוף גם EqualityComparers יותר מעניינים:

1
2
3
4
5
6
7
8
9
string[] tokens = new string[] {"The", "quick", "brown", "fox","jumps", "over", "the", "lazy","dog"};
string[] otherTokens = new string[] { "The", "Quick", "Brown", "fox","jUmps", "ovEr", "the", "lazy","Dog" };
IStructuralEquatable tokensStructuralEquatable = tokens;
if(tokensStructuralEquatable.Equals(otherTokens, StringComparer.CurrentCultureIgnoreCase))
{
// True
}

שבוע מובנה טוב!

שתף

264. Computing a MD5 hash

מנגנון הHashing (גיבוב) הוא מנגנון המאפשר לבצע “הצפנה” חד כיוונית.

הרעיון הוא להמיר מידע בגודל לא מוגבל, לאובייקט כלשהו בעל גודל קבוע המייצג אותו.

ההמרה מתבצעת בצורה שהיא רגישה לרעשים, כך שאם מתבצע שינוי קטן במידע, יתבצע שינוי דרסטי בHash.

הדבר שימושי במספר מקומות: לדוגמה, בהשוואת קבצים, בד”כ בדיקה שהHash שלהם לפי אלגוריתם מסוים הוא שווה, מספיק אמינה כדי להניח שהקבצים זהים.

בנוסף, בשמירת סיסמאות: יש לא מעט Databaseים שלא שומרים את הסיסמאות של המשתמשים שלהם, אלא רק את הHash של הסיסמאות, כך שאם מישהו נכנס לDatabase, הוא לא יוכל לקבל מידע רגיש. כאשר משתמש מנסה להתחבר למערכת, מתבצעת השוואה של הסיסמה שלו לHash ששמור במערכת.

אחד אלגוריתמי הHash הנפוצים בעולם הוא אלגוריתם הMD5.

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

בFramework יש לנו תמיכה במספר אלגוריתמי גיבוב, ביניהם גם באלגוריתם MD5.

השימוש בו הוא כזה:

1
2
3
4
5
6
MD5 algorithm = MD5.Create();
using (FileStream stream = newFileStream(@"MyFile.txt",FileMode.Open))
{
byte[] hash = algorithm.ComputeHash(stream);
}

מאחר ואלגוריתם MD5 מחזיר לנו מערך של 16 בתים, בד"כ נוח להסתכל על הHash כGuid:

1
2
3
4
5
6
7
8
9
10
11
Guid hashGuid;
MD5 algorithm = MD5.Create();
using (FileStream stream = newFileStream(@"MyFile.txt",FileMode.Open))
{
byte[] hash = algorithm.ComputeHash(stream);
hashGuid = new Guid(hash);
}
Console.WriteLine(hashGuid);//1245511d-d93c-9054-5eb0-04a9a5ac51c9

שתהיה לכולנו שנה מעולה ומלאת אמון!

שתף

263. Guid TryParse

בהמשך לפעם הקודמת,

אם התמזל מזלכם ואתם מתכנתים בFramework 4.0 ומעלה, תוכלו לנסות להמיר מחרוזת לGuid ולהימנע מהException שבדרך, מבלי להתעסק עם ביטויים רגולריים, והביצועים שבדבר, באמצעות הפונקציה Guid.TryParse:

1
2
3
4
if (Guid.TryParse(value, out result))
{
// ...
}

אם ננסה להסתכל בReflector מה קורה כאן, נראה קוד מסובך שמנסה לפרסר את הGuid, בדומה לConstructor המסובך של Guid בגרסאות הקודמות של הFramework.

עוד סיבה נחמדה לעבור לFramework 4.0 😃

שתהיה שנה ש(כמעט) אין כמוה

שתף

262. Checking if a string is a valid Guid

לפעמים אנחנו מקבלים מחרוזת ומעוניינים לוודא שהיא מהווה Guid תקין.

הדרך הכי פשוטה לעשות זאת היא באמצעות תפיסת Exception:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static bool TryParse(string value, out Guid result)
{
try
{
result = new Guid(value);
}
catch (FormatException)
{
result = Guid.Empty;
return false;
}
return true;
}

אלא שזה די דוחה לתפוס Exception כדי לבדוק האם יצירת הGuid הצליחה או לא.

היינו מעדיפים לוודא לפני הפרסור האם מדובר הפרסור הולך להצליח או לא…

אז ננסה:

ניצור איזה Regex שבודק האם Guid הוא תקין או לא:

הRegex נראה כמו משהו כזה:

^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$

[מבוסס על הPattern מכאן]

אז נבדוק:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static readonly Regex mGuidRegex =
new Regex
(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$",
RegexOptions.Compiled);
private static bool TryParse(string value, out Guid result)
{
if (mGuidRegex.IsMatch(value))
{
result = new Guid(value);
return true;
}
else
{
result = Guid.Empty;
return false;
}
}

שימו לב שהRegex הזה סבבה והכל, אבל לא מתאים לכל הFormatים החוקיים של Guidים.

אפשר לנסות לשפר את הRegex, אבל נשים לב שככל שננסה לשפר אותו יותר, כך וואלידאציה עליו תהיה יותר כבדה…

בסופו של דבר, אם אנחנו יודעים שהGuidים שאנחנו מצפים לקבל הם בפורמט ספציפי, נוכל לכתוב Regex מתאים לו ולחסוך את תפיסת הException. במידה ולא נוכל לאפיין את כל הGuidים שאנחנו מצפים לקבל, כנראה הפתרון הכי טוב הוא להשתמש בtry-catch, מאחר והוואלידאציה תעלה לנו לא פחות מתפיסת הException מבחינת ביצועים…

המשך יום כמעט חח"ע טוב.

שתף

261. System.Guid

המבנה ששמו Guid מייצג מזהה ייחודי גלובלי – ערך המורכב מ16 בתים, שאם יווצר בצורה אקראית, יהיה כמעט בוודאות ייחודי בעולם.

Guidים בד”כ משמשים כמזהים בכל מיני מקומות: בDatabaseים ובאפליקציות.

אם תחשבו על זה, יש 2 בחזקת 128 אפשרויות לGuidים.

כדי ליצור Guid חדש, ניתן להשתמש בפונקציהGuid.NewGuid:

1
2
Guid myGuid = Guid.NewGuid();
string guidAsString = myGuid.ToString(); // d8f07443-dffa-42bf-857f-2d041e60177a

כשהוא מיוצג כמחרוזת, Guid מיוצג כמספר הקסה דצימלי עם 32 ספרות, עם מקפים אופציונאליים אחרי המקום השמיני, ה12, ה16 וה20.

המחרוזת כולה יכולה להיות עטופה בסוגריים עגולים או מסולסלים:

1
2
3
4
5
6
7
8
9
10
Guid firstGuid =
new Guid("{d8f07443-dffa-42bf-857f-2d041e60177a}");
Guid secondGuid =
new Guid("d8f07443dffa42bf857f2d041e60177a");
if (firstGuid == secondGuid)
{
// True
}

האופרטור == משווה את הGuidים לפי הערכים אותם הם מייצגים (ראו גם טיפים 76-80)

ניתן להפוך Guid לייצוג שלו ע"י מערך של 16 בתים:

1
byte[] bytes = myGuid.ToByteArray();

ולהפך:

1
Guid myGuid = new Guid(bytes);

המשך יום ייחודי טוב!

שתף