Прежде всего: лучшая ставка для ответа на этот вопрос была бы попробуйте .Вскоре вы увидели, что когда вы пытались вызвать функцию во второй форме, она не работает.
Тем не менее, давайте немного разберёмся с этим.У нас есть
public static bool IsNullOrEmpty<TKey, TValue>(
this Dictionary<TKey, List<TValue>> dictionary, TKey key)
против
public static bool IsNullOrEmpty<TKey, TValue>(
this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : List<TValue>
Почему второй неверен?Ну, какой аргумент типа вы хотели бы передать для TValue
?Предположим, у нас в руке Dictionary<string, List<int>>
.Что такое TValue
, который мы можем использовать?Это не int
, потому что это не соответствует ограничению: int
не происходит от List<int>
.Но это не List<int>
, потому что List<int>>
не происходит от List<TValue>
, то есть List<List<int>>
.
Итак, теперь мы знаем, почему второе неверно.Давайте теперь ответим на дополнительные вопросы:
При каких обстоятельствах имеет смысл этот вид "рекурсивного" ограничения?
Предположим, мы пытаемся найти максимум ключ в словаре:
public static TKey MaxKey<TKey, TValue>(
this Dictionary<TKey, TValue> dictionary)
where TKey : IComparable<TKey>
{
if (dictionary.Count == 0) throw ...
TKey best = default(TKey);
bool first = true;
foreach(TKey k in dictionary.Keys)
{
if (first || best.CompareTo(k) < 0)
best = k;
first = false;
}
return best;
}
Здесь имеет смысл ограничить TKey
до IComparable<TKey>
;мы собираемся сравнить ключи.
Как другие люди могут использовать этот шаблон и злоупотреблять им?
См. https://blogs.msdn.microsoft.com/ericlippert/2011/02/03/curiouser-and-curiouser/ для многих примеров.
Почему первый метод не идеален?
Поскольку он не обрабатывает эти два случая:
Во-первых, предположим, что у нас есть многоцелевое выражение, но оно не из ключей для списков:
Dictionary<string, int[]>
или
Dictionary<string, Stack<int>>
или
Dictionary<string, IEnumerable<int>>
или что-либо еще.
Во-вторых, это также не относится к делу
class MyList : List<int> {}
...
Dictionary<string, MyList>
хотя этот случай довольно редкий;вам не следует расширять List<T>
как обычно.
Как правильно реализовать метод словаря для максимальной универсальности?
Вот один из способов:
public static bool IsEmpty(this IEnumerable items)
{
// EXERCISE: Why is this implementation bad?
// EXERCISE: Can you improve it?
foreach(var item in items)
return false;
return true;
}
public static bool IsNullOrEmpty<TKey, TValue>(
this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : IEnumerable
{
return !dictionary.ContainsKey(key) || dictionary[key].IsEmpty();
}
Для большей общности вы можете использовать IDictionary
вместо Dictionary
.