Я специально думаю о родовом классе HashSet<T>
. Он реализует несколько интерфейсов, но ни один не предоставляет правильную семантику набора. В частности, ни один из них не поддерживает метод Add
, возвращающий bool
. (ICollection<T>
поддерживает void Add
, который может использоваться в крайнем случае.) Эти интерфейсы также не поддерживаются общими операциями над множествами, такими как объединения и пересечения. (Хотя нужно сказать, что некоторые из этих операций доступны через расширения до IEnumerable<T>
.)
Это означает, что класс может использоваться только как набор с его прямой реализацией. То есть, вы не можете сделать что-то вроде этого:
ISet<int> = new HashSet<int>;
Во всяком случае, не так, как я знаю. Так что же побудило вас отказаться от этого?
Возможно, самое важное в этом заключается в следующем: даже если вы можете привести HashSet<T>
к ICollection<T>
и др., Вы теряете семантическую ценность в предоставляемом вами API. То есть потребители вашего API не имеют никаких признаков того, что они работают с набором. Таким образом, хотя вы можете позвонить ICollection<T>.Add
и обойтись, люди будут сбиты с толку, если они попытаются добавить элемент дважды, а он не работает. Настроенный интерфейс даст людям правильные ожидания.