IComparable с перечислениями, отправленными как параметры универсального типа (C#)

Возможный дубликат:
Когда два перечисления равны на С#?

У меня есть следующие классы как часть простого конечного автомата.

Обратите внимание, что все параметры универсального типа ДОЛЖНЫ быть перечислением. Это было реализовано в конструкторах (здесь не показано).

// Both [TState] and [TCommand] will ALWAYS be enumerations.
public class Transitions<TState, TCommand>: List<Transition<TState, TCommand>>
{
    public new void Add (Transition<TState, TCommand> item)
    {
        if (this.Contains(item))
            throw (new InvalidOperationException("This transition already exists."));
        else
            this.Add(item);
    }
}

// Both [TState] and [TCommand] will ALWAYS be enumerations.
public class Transition<TState, TCommand>
{
    TState From = default(TState);
    TState To  = default(TState);
    TCommand Command = default(TCommand);
}

public sealed class TransitionComparer<TState>:
    IComparer<TState>
{
    public int Compare (TState x, TState y)
    {
        int result = 0;

        // How to compare here since TState is not strongly typed and is an enum?
        //    Using ToString seems silly here.

        result |= x.From.ToString().CompareTo(y.From.ToString());
        result |= x.To.ToString().CompareTo(y.To.ToString());
        result |= x.Command.ToString().CompareTo(y.Command.ToString());

        return (result);
    }
}

Вышеприведенное действительно компилируется, но я не уверен, что это правильный способ обработки перечислений, которые были переданы в качестве параметров универсального типа.

Примечание. Функция сравнения не должна учитывать порядок. Скорее, ему нужно проверить наличие точных дубликатов.


person Raheel Khan    schedule 24.09.2012    source источник


Ответы (2)


Примечание. Функция сравнения не должна учитывать порядок. Скорее, ему нужно проверить наличие точных дубликатов.

В этом случае вам не следует реализовывать IComparer<T>. Вы должны реализовать IEqualityComparer<T> или, проще говоря, заставить Transition реализовать IEquatable<Transition>.

Обратите внимание, что в настоящее время вы не используете TransitionComparer в остальной части кода, насколько мы можем видеть. Похоже, вам не нужно писать свой собственный код сравнения для каждого значения перечисления - вы просто пытаетесь их объединить. К сожалению, перечисления не реализуют IEquatable<T>, что делает это немного сложнее без бокса - насколько это критично для производительности?

Вот пример реализации равенства для Transition:

public class Transition<TState, TCommand>
    : IEquatable<Transition<TState, TCommand>>
{
    // I assume in reality these are properties?
    TState From = default(TState);
    TState To  = default(TState);
    TCommand Command = default(TCommand);

    public override bool Equals(object other)
    {
        return Equals(other as Transition<TState, TCommand>);
    }

    public bool Equals(Transition<TState, TCommand> other)
    {
        if (other == null)
        {
            return false;
        }
        return From.Equals(other.From) &&
               To.Equals(other.To) &&
               Command.Equals(other.Command);
    }

    public int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + From.GetHashCode();
        hash = hash * 31 + To.GetHashCode();
        hash = hash * 31 + Command.GetHashCode();
        return hash;
    }
}

РЕДАКТИРОВАТЬ: Чтобы избежать упаковки для равенства, я подозреваю, что вам либо понадобится какой-то делегат, чтобы получить базовое значение (и если вам нужно поддерживать значения с разными базовыми типами, это довольно болезненно) или потенциально использовать что-то вроде Unconstrained Melody, в котором используется перезапись IL для обеспечения соблюдения ограничения во время компиляции. и позволяет более эффективно проверять равенство на основе базового значения.

person Jon Skeet    schedule 24.09.2012
comment
Спасибо. Я пишу это как библиотеку для будущих проектов, некоторые из которых могут быть критическими для производительности. Вы бы подошли к этому как-то по-другому в этом случае? И да, вы правы, сам компаратор нигде не используется. - person Raheel Khan; 24.09.2012
comment
@RaheelKhan: я добавлю правку. - person Jon Skeet; 24.09.2012
comment
Спасибо, Джон, за исправление моей ошибки, реализацию и ссылку на UM! Я сделаю прототип с боксом, а тем временем посмотрю на UM. - person Raheel Khan; 24.09.2012
comment
Я тщетно пытался получить доступ к Unconstrained Melody с нескольких компьютеров и задавался вопросом, не работает ли он для других. - person Raheel Khan; 24.09.2012
comment
@RaheelKhan: у меня это работает. Вам вообще не подходит Google Code? - person Jon Skeet; 24.09.2012
comment
Кажется, код Google недоступен для меня только через HTTP. Я должен быть в состоянии найти SVN и подключиться. - person Raheel Khan; 24.09.2012
comment
@RaheelKhan: Хорошо, это svn checkout http://unconstrained-melody.googlecode.com/svn/trunk/ unconstrained-melody-read-only - person Jon Skeet; 24.09.2012
comment
Просто возвращаюсь к старым вопросам. Есть ли конкретная причина, по которой вы использовали эти конкретные числа в GetHashCode? Я не очень понимаю, как это должно быть реализовано. - person Raheel Khan; 21.06.2013
comment
@RaheelKhan: Это только те числа, которые, как мне кажется, хорошо работают. Математика, стоящая за этим, сложна, IIRC. Тот факт, что оба числа являются простыми, вполне может быть полезен, и умножение на 31 может быть оптимизировано для сдвига, а затем вычитания. В основном я беру этот подход у Джоша Блоха :) - person Jon Skeet; 21.06.2013

я ничего не вижу кроме

(int)(object)x.From.CompareTo((int)(object)y.From);

Перечисления упакованы. Я не знаю, как этого можно было избежать.

person Stefan Steinegger    schedule 24.09.2012
comment
Это предполагает, что мы знаем, что базовый тип — int, конечно. - person Jon Skeet; 24.09.2012
comment
Истинный. Но все же может работать в конкретном проекте. Это не будет код фреймворка. - person Stefan Steinegger; 24.09.2012
comment
Это действительно может сработать - я просто думаю, что это стоит отметить как ограничение. - person Jon Skeet; 24.09.2012