Какой использовать?IComparable общий или не общий в этом сценарии - PullRequest
4 голосов
/ 30 мая 2011

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

(2). Является ли этот шаблон компаратора корректным, имея компаратор внутри класса dataitem? Потому что пользователь предоставит новый 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; }
}

Ответы [ 3 ]

1 голос
/ 30 мая 2011

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

  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, и они имеют согласованную структуру и логику сравнения.

EDIT Я только что увидел, что у вас есть логика сравнения «Имеет» вместо чистой«Есть» сравнимо.Это должно быть исправлено (удалите ссылку на компаратор из элементов данных) - если нет, вы правы - дерево может сломаться ...

EDIT2 Если вам нужноэлементы данных должны иметь гибкую логику сравнения (т.е. изменять способ их сравнения, что странно, так что подумайте об этом), тогда BST должен иметь ссылку на экземпляр компаратора, который вы намереваетесь использовать с ним: либо поместитеэлементы, которые обертывают компаратор и фактический элемент или помещают его в качестве свойства BST и используют его для каждого элемента данных при принятии решения, к какой ветви переходить.

1 голос
/ 30 мая 2011

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

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

и 2-го вопроса?

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

1 голос
/ 30 мая 2011

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

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