Неявные преобразования C# и оператор ==

Некоторый код для контекста:

class a
{

}

class b
{
    public a a{get;set;}
    public static implicit operator a(b b)
    {
        return b.a;
    }
}

  a a=null;
  b b=null;
  a = b;

  //compiler: cannot apply operator '==' to operands of type tralala...
  bool c = a == b; 

Можно ли использовать оператор == для экземпляров разных типов, где один может неявно преобразовываться в другой? Что я пропустил?

Редактировать:
Если типы должны быть одинаковыми, вызывающими ==, то почему

int a=1;
double b=1;
bool c=a==b; 

работает?


person Arnis Lapsa    schedule 21.05.2009    source источник
comment
Я бы предположил, что ваш пример int/double работает, потому что они являются типами значений, а не ссылочными типами...   -  person Rowland Shaw    schedule 21.05.2009
comment
Это может быть причиной. Единственная проблема заключается в том, что если это так, это не объясняет, почему именно ссылочный тип не может выполнять преобразование неявно, как это могут делать типы значений.   -  person Arnis Lapsa    schedule 21.05.2009


Ответы (5)


Оператор implicit работает только для присваивания.

Вы хотите перегрузить оператор равенства (==) как таковой:

class a
{
    public static bool operator ==(a x, b y)
    {
        return x == y.a;
    }

    public static bool operator !=(a x, b y)
    {
        return !(x == y);
    }
}

class b
{
    public a a{get;set;}
    public static implicit operator a(b b)
    {
        return b.a;
    }
}

Затем это должно позволить вам сравнить два объекта типа a и b, как это предлагается в вашем посте.

var x = new a();
var y = new b();
bool c = (x == y); // compiles

Примечание.

Я рекомендую просто переопределить методы GetHashCode и Equals, как предупреждает компилятор, но поскольку вы, похоже, хотите подавить их, вы можете сделать это следующим образом.

Измените объявление класса a на:

#pragma warning disable 0660, 0661
class a
#pragma warning restore 0660, 0661
{
    // ...
}
person Noldorin    schedule 21.05.2009
comment
и в этом случае выражение a == b скомпилируется без проблем. - person Galilyou; 21.05.2009
comment
Это кажется правильным ответом. Типа понял и это. Только - я думаю, что в этом случае перегрузка оператора будет излишней (она также требует перегрузки функций gethash() и equals() :/). - person Arnis Lapsa; 21.05.2009
comment
Да, нет никаких ограничений на определение или использование пользовательских (перегруженных) операторов для пользовательских типов. - person Noldorin; 21.05.2009
comment
@Arnis L: К счастью, это всего лишь предупреждения. Вы можете переопределить их, если хотите (Equals может иметь ту же логику, что и ==, а GetHashCode может просто возвращать a.GetHashCode() ^ b.GetHashCode()), или вы можете игнорировать/подавлять предупреждения, если вам все равно. - person Noldorin; 21.05.2009
comment
Есть ли способ подавить предупреждение через код (где-то видел что-то подобное)? - person Arnis Lapsa; 21.05.2009
comment
@Arnis L: Да, смотрите мой обновленный пост. Однако не уверен, что это намного больше усилий, чтобы переопределить их простой реализацией... - person Noldorin; 21.05.2009
comment
Просветите меня, почему этот код, который я добавил после Edit:, работает, и вы обязательно получите принятый ответ. :) - person Arnis Lapsa; 21.05.2009
comment
Ах да, я пропустил часть вашего вопроса, извините. Я считаю, что это функция, специфичная для компилятора (в System.Int32 или System.Double не определены перегрузки операторов). В этом случае b (двойной) преобразуется в int перед приравниванием объектов того же типа. (может быть наоборот - простой тест покажет вам.) Надеюсь, теперь все это стало для вас понятным. - person Noldorin; 21.05.2009
comment
Int преобразуется в double. Если бы число double было преобразовано в число int, то значение double 2.1 было бы равно int 2! - person Eric Lippert; 21.05.2009
comment
@Eric: Да, расширение, а не сужение преобразования имеет гораздо больше смысла. Порядок операндов также не имеет значения. - person Noldorin; 21.05.2009

Можно ли использовать оператор == для экземпляров разных типов, где один может неявно преобразовываться в другой?

Да.

Что я пропустил?

Вот соответствующая часть спецификации. Вы пропустили выделенное слово.

Предопределенные операторы равенства ссылочного типа требуют, чтобы оба операнда были значениями ссылочного типа или литералом null. Более того, существует стандартное неявное преобразование типа одного операнда в тип другого операнда.

Пользовательское преобразование по определению не является стандартным преобразованием. Это ссылочные типы. Следовательно, предопределенный оператор равенства ссылочного типа не является кандидатом.

Если типы должны быть одинаковыми, вызывая ==, то почему работает [double == int]?

Ваше предположение, что типы должны быть одинаковыми, неверно. Существует стандартное неявное преобразование из int в double и оператор равенства, который принимает два числа типа double, так что это работает.

Я думаю, вы также пропустили этот момент:

Использование предопределенных операторов равенства ссылочного типа для сравнения двух ссылок, о которых известно, что они различны во время компиляции, является ошибкой времени компиляции. Например, если типы операндов времени компиляции являются двумя типами классов A и B, и если ни A, ни B не являются производными от другого, то два операнда не могут ссылаться на один и тот же объект. Таким образом, операция считается ошибкой времени компиляции.

person Eric Lippert    schedule 21.05.2009

Я бы предположил, что вам нужно фактически переопределить оператор == для интересующих вас типов. Будет ли среда компиляции/выполнения по-прежнему жаловаться, даже если типы неявно конвертируемы, - это то, с чем вам придется поэкспериментировать.

public static bool operator ==(a a, b b)
    {
        //Need this check or we can't do obj == null in our Equals implementation
        if (((Object)a) == null)
        {
            return false;
        }
        else
        {
            return a.Equals(b);
        }
    }

В качестве альтернативы просто используйте реализации Equals, как предлагает ole6ka, и убедитесь, что реализация выполняет приведение типов, которое вам нужно.

person RobV    schedule 21.05.2009

http://msdn.microsoft.com/en-us/library/8edha89s.aspx

В каждом случае один параметр должен быть того же типа, что и класс или структура, объявляющая оператор (...)

person A. M.    schedule 21.05.2009

Использовать это

 bool c = a.Equals(b);
person ole6ka    schedule 21.05.2009
comment
В конкретном случае это бесполезно, потому что я злоупотребляю лямбда-выражениями и шаблоном построителя для создания динамического установщика свойств, например: factory.Object.With(object=>object.MyProp==newPropValue).Create() - person Arnis Lapsa; 21.05.2009