143. IsAssignableFromGenericInterface extension method

בהמשך לטיפ של אתמול,

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

למרבה הצער, בדוגמאות הבאות, הפונקציה IsAssignableFrom תכשל:

דוגמה ראשונה:

פשוט ננסה להשתמש בIsAssignableFrom על unbound generic type…

יש לנו מחלקה כזאת

1
public class DumbCollection : ICollection<int>

ונקבל:

1
2
3
4
if(typeof(ICollection<>).IsAssignableFrom(typeof(DumbCollection)))
{
// False
}

דוגמה שנייה:

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

1
2
3
4
5
6
7
Type unboundList =
typeof (List<int>).GetGenericTypeDefinition();
if (typeof(ICollection<>).IsAssignableFrom(unboundList))
{
// False
}

אפילו זה לא עובד…


אם ננסה להבין למה זה קורה, נראה כי הממשקים שמממש unboundList הם לא IsAssignableFrom מtypeof(ICollection<>):

1
2
3
4
5
6
7
IEnumerable<Type> types =
unboundList.GetInterfaces().Where(x => typeof(ICollection<>).IsAssignableFrom(x));
if (types.Any())
{
// False
}

אבל באופן מפתיע, אם נעשה משהו כזה:

(נרמלנו את הType)

1
2
3
4
5
6
7
8
9
IEnumerable<Type> types =
unboundList.GetInterfaces().
Where(x => x.IsGenericType &&
typeof(ICollection<>).IsAssignableFrom(x.GetGenericTypeDefinition()));
if (types.Any())
{
// True
}

יא-אללה.


בקיצור, נוכל לפתור את הסוגיה ע"י כתיבת Extension Method נחמד:

1
2
3
4
5
6
7
8
9
10
11
public static bool IsAssignableFromGenericInterface(thisType genericInterface, Type type)
{
IEnumerable<Type> collectionInterfaces =
from currentInterface in type.GetInterfaces()
where currentInterface.IsGenericType
let nonGeneric = currentInterface.GetGenericTypeDefinition()
where genericInterface.IsAssignableFrom(nonGeneric)
select currentInterface;
return collectionInterfaces.Any();
}

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

1
2
3
4
5
6
7
8
9
if(typeof(ICollection<>).IsAssignableFromGenericInterface(typeof(DumbCollection)))
{
// True
}
if(typeof(ICollection<>).IsAssignableFromGenericInterface(typeof(List<int>)))
{
// True
}

כמובן, נוכל לבצע בכניסה למתודה וולידציה על הפרמטרים, כגון האם genericInterface הוא unbound (ע"י IsGenericTypeDefinition) וכו’.

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

יום טיפוסי טוב

שתף