72. Enum HasFlag extension

ראינו בעבר (טיפ מספר 22) כיצד אנחנו יכולים לבדוק שיש לEnum איזשהו Flag.

הדרך הייתה כזו:

1
2
3
4
if ((direction & Direction.South) == Direction.South)
{
// ...
}

הבעיה היא שלא כל כך כיף לכתוב את זה.

עכשיו, אחרי שלמדנו על Extension Methods נוכל לעשות משהו עם Syntax טיפה יותר יפה:

ניצור Extension Method כזה:

1
2
3
4
5
6
public static bool HasFlag(this Enum givenEnum, Enum flagEnum)
{
ulong givenValue = Convert.ToUInt64(givenEnum);
ulong flagValue = Convert.ToUInt64(flagEnum);
return ((givenValue & flagValue) == flagValue);
}

ואז נוכל לכתוב קוד כזה:

1
2
3
4
if (direction.HasFlag(Direction.South))
{
// ...
}

שימו לב שהסינטקס יותר יפה, אבל יש פה מספר בעיות:

  1. יש Boxing לEnum בקריאה לפונקציה
  2. אנחנו ממירים את הערכים לulong
  3. אין לנו התחייבות ששני הEnumים מאותו סוג

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

אגב, בFramework 4.0 הוסיפו מתודה לEnum שנקראת HasFlag אשר בודקת אם יש לEnum דגל. המימוש הוא מאוד דומה למה שכתבנו פה.

בכל מקרה זה בעיקר נחמד.

הערה:

ניתן לעשות כך :

1
2
3
4
5
6
7
public static bool HasFlag<T>(this T value, T flag)
where T : IConvertible
{
long underValue = value.ToInt64(null);
long underFlag = flag.ToInt64(null);
return ((underFlag & underValue) == underFlag);
}

כך בעצם נחסוך כמה דברים :

  1. גנריות – נוכל להשתמש בפונקציה הנ"ל על כל דבר שמממש IConvertible – אפילו Stringים מממשים את זה, וכל הPrimitives.
  2. אין Boxing
  3. יש התחיבות ששני הEnumים הם מאותו הסוג.
שתף