Что использовать? Сопоставимый общий или не-общий в этом сценарии

#.net #generics #collections

#.net #общие #Коллекции

Вопрос:

У меня есть общий BST и класс DataItem, которые будут действовать как значение TreeNode.

 public class BinarySearchTree<T> : ICollection<T>  where T : IComparable
{

}

public class EnglishDictionaryWord
    : IComparer<EnglishDictionaryWord>, IComparable<EnglishDictionaryWord>
{
    public EnglishDictionaryWord(string word, WordCompareType type)
    {
        Word = word;
        Length = Word.Length;
        _compareType = type;
    }

    public int Compare(EnglishDictionaryWord x, EnglishDictionaryWord y)
    {
        if (_compareType == WordCompareType.Length)
            return new WordLengthComparer().Compare(x, y);
        else if (_compareType == WordCompareType.Lexical)
            return new WordLexicalComparer().Compare(x, y);
        else
            throw new InvalidOperationException("Unsupported Comparison type");
    }

    public int CompareTo(EnglishDictionaryWord obj)
    {
        return Compare(this, obj);
    }
} 

public class WordLengthComparer : IComparer<EnglishDictionaryWord>
{
    public WordLengthComparer()
    {

    }
    public int Compare(EnglishDictionaryWord x, EnglishDictionaryWord y)
    {
        return x.Length - y.Length;
    }
}

and similar Lexical comparer class.
  

Теперь, когда я пытаюсь использовать:

 BinarySearchTree<EnglishDictionaryWord> _tree = 
    new BinarySearchTree<EnglishDictionaryWord>();
  

Я получаю ошибку компиляции:

Тип ‘DsLib.EnglishDictionaryWord’ не может использоваться в качестве параметра типа ‘T’ в универсальном типе или методе ‘DsLib.BinarySearchTree’. Неявное преобразование ссылки из ‘DsLib.EnglishDictionaryWord’ в ‘System’ отсутствует.IComparable’.

Если я попытаюсь сделать

 public class BinarySearchTree<T> : ICollection<T>  where T : IComparable<T>
  

затем я получаю эту ошибку компиляции о недоступности преобразования бокса.

Тип ‘T’ не может использоваться в качестве параметра типа ‘T’ в универсальном типе или методе ‘DsLib.BinaryTreeNode’. Нет преобразования бокса или преобразования параметра типа из ‘T’ в ‘System.IComparable’.

У меня есть 2 вопроса:

(1). Я запутался в реализации generics. Может ли кто-нибудь подробно рассказать, как это исправить? и общий шаблон, чтобы избежать подобных ошибок в будущем. Когда использовать IComparable<T> и когда IComparable .

(2). Корректен ли этот шаблон сравнения, имеющий comparer внутри класса dataitem? Потому что пользователь предоставит new EnglishWord для вставки в дерево. Потенциально он может использовать разные средства сравнения для каждого слова. Тогда это приведет к разрыву дерева.

РЕДАКТИРОВАТЬ: Добавлен код класса BSTNode

 public class BinaryTreeNode<T> where T : IComparable
{
    public BinaryTreeNode(T value)
    {
        Value = value;
    }

    public T Value { get; protected internal set; }
    public BinaryTreeNode<T> Right { get; protected internal set; }
    public BinaryTreeNode<T> Left { get; protected internal set; }
    public BinaryTreeNode<T> Parent { get; protected internal set; }

    public int Height { get; protected internal set; }
}
  

Комментарии:

1. Я нигде не вижу DsLib.BinaryTreeNode

2. @Kamarey Мой вопрос касается IComparable и IComparable<T> , а не IComparer<T> и IComparable<T>

Ответ №1:

Я попробовал ваш код со следующими определениями:

   public class BinarySearchTree<T>
      : ICollection<T>
      where T : IComparable<T>

  public class EnglishDictionaryWord
      : IComparer<EnglishDictionaryWord>,
        IComparable<EnglishDictionaryWord>

  public class WordLengthComparer
      : IComparer<EnglishDictionaryWord>
  

и это работает просто отлично — компилируется, выполняется и т.д. … (.NET 4.0, c #):

  BinarySearchTree<EnglishDictionaryWord> _tree = 
     new BinarySearchTree<EnglishDictionaryWord>();
  

И чтобы ответить на другие ваши вопросы:

  1. Вы всегда должны отдавать предпочтение IComparable<T> вместо IComparable . Это быстрее и менее подвержено ошибкам (нет кастинга / упаковки / распаковки) и т.д… Что касается вашего вопроса: Зачем это требуется?все просто — IComparable<T> и IComparable — это разные типы (у них похожие названия, но пусть это вас не смущает — типы разные). Итак, вам просто нужно поместить один и тот же тип везде, где есть ссылка.

  2. Данные, которые вставляются в дерево, должны иметь логику сравнения. Когда вы определяете дерево, вы точно указываете, с какими типами элементов оно будет работать, поэтому до тех пор, пока этот объект существует, вы не сможете добавить в него какой-то совершенно другой тип. Например, если вы определили:

    BinarySearchTree<EnglishDictionaryWord> _tree;

вы не можете добавить к _tree чему-то еще, например SpanglishDictionaryWord , поэтому дерево сохраняет свою структуру правильно, потому что добавляются только элементы EnglishDictionaryWord , и они имеют определенную структуру и логику сравнения, которая согласована.

РЕДАКТИРОВАТЬ Я только что увидел, что у вас есть логика сравнения «Имеет» вместо чистой сопоставимости «Является». Это должно быть исправлено (удалите ссылку на средство сравнения из элементов данных) — если нет, вы правы — дерево может быть сломано…

ОТРЕДАКТИРУЙТЕ 2, если вам нужно, чтобы элементы данных имели гибкую логику сравнения (т.Е. измените способ их сравнения, что странно, так что подумайте об этом), тогда BST должен иметь ссылку на экземпляр средства сравнения, которое вы собираетесь использовать с ним: либо поместите элементы, которые переносят средство сравнения и фактический элемент, либо поместите средство сравнения элементов как свойство BST и используйте это для каждого элемента данных при принятии решения о том, в какую ветку перейти.

Комментарии:

1. @Daniel да, спасибо, он компилируется, если я меняю на IComparable<T> везде в классах / методах, связанных с деревом. Это нормально. Можете ли вы ответить, почему это требуется, и на 2-й вопрос? в основном я стремлюсь понять причины, а не делать проб и ошибок в будущем 🙂

2. 1 для «Имеет» ..»Является». Пожалуйста, посмотрите мой комментарий к ответу @David B. и увеличьте свой ответ в этой части. Спасибо.

Ответ №2:

Вам тоже нужно измениться BinaryTreeNode :

 public class BinaryTreeNode<T> where T : IComparable<T>
  

Комментарии:

1. Я тоже это сделал. Та же ошибка. Все еще вопрос, почему? необходимы некоторые пояснения по заданным вопросам.

Ответ №3:

Он компилируется, если я изменяю на IComparable<T> везде в классах / методах, связанных с деревом. Зачем это требуется?

IComparable<T> не наследуется от IComparable .

и 2-й вопрос?

Вы правы — если разные элементы имеют разные типы сортировки, список будет работать неправильно. Лучшим шаблоном является то, чтобы тип имел единственное поведение упорядочивания по умолчанию, а затем разрешал коллекции принимать и использовать альтернативные IComparers. Чтобы увидеть это в действии, изучите перегрузки Enumerable.OrderBy

Комментарии:

1. То есть вы имеете в виду TreeOfEnglishWords. OrderBy(новый WordLengthComparer()) что-то вроде этого? Моему дереву также требуется средство сравнения во время операций добавления / содержания. Итак, должно ли дерево принимать средство сравнения по умолчанию в своем конструкторе?

2. Да, конструктор. Аналогично тому, как универсальный словарь может принимать IEqualityComparer<T> в своем конструкторе. msdn.microsoft.com/en-us/library/6918612z.aspx