Вопрос по struct, c# – C # - метод Value Type Equals - почему компилятор использует отражение?

15

Я только что натолкнулся на что-то довольно странное для меня: когда вы используете метод Equals () для типа значения (и, если этот метод, конечно, не был переопределен), вы получаете что-тоочень-очен slow - поля сравниваются один к одному с помощью отражения! Как в

<code>public struct MyStruct{
   int i;
}

   (...)

   MyStruct s, t;
   s.i = 0;
   t.i = 1;
   if ( s.Equals( t ))   /*  s.i will be compared to t.i via reflection here. */
      (...)
</code>

Мой вопрос: почему компилятор C # не генерирует простой метод для сравнения типов значений? Что-то вроде (в определении MyStruct):

<code>   public override bool Equals( Object o ){
      if ( this.i == o.i )
         return true;
      else
         return false;
   }
</code>

Компилятор знает, каковы поля MyStruct во время компиляции, почему он ожидает до времени выполнения для перечисления полей MyStruct?

Очень странно для меня.

Благодарность :

ДОБАВЛЕНО: Извините, я просто понимаю, что, конечно,Equals - это не ключевое слово языка, а метод времени выполнения ... Компилятор совершенно не знает об этом методе. Так что имеет смысл использовать рефлексию.

"Чтобы использовать стандартную реализацию Equals, ваш тип значения должен быть упакован и передан как экземпляр ссылочного типа System.ValueType. Затем метод Equals использует отражение для выполнения сравнения." - msdn.microsoft.com/en-us/library/ff647790.asp MrPhil

Ваш Ответ

2   ответа
10

public override bool Equals(object obj)
{
    if (obj == null)
    {
        return false;
    }
    RuntimeType type = (RuntimeType) base.GetType();
    RuntimeType type2 = (RuntimeType) obj.GetType();
    if (type2 != type)
    {
        return false;
    }
    object a = this;
    if (CanCompareBits(this))
    {
        return FastEqualsCheck(a, obj);
    }
    FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < fields.Length; i++)
    {
        object obj3 = ((RtFieldInfo) fields[i]).InternalGetValue(a, false);
        object obj4 = ((RtFieldInfo) fields[i]).InternalGetValue(obj, false);
        if (obj3 == null)
        {
            if (obj4 != null)
            {
                return false;
            }
        }
        else if (!obj3.Equals(obj4))
        {
            return false;
        }
    }
    return true;
}

Когда возможно, будет сделано побитовое сравнение (обратите внимание на CanCompareBits и FastEqualsCheck, оба из которых определены как InternalCall. JIT предположительно вставит соответствующий код здесь. Что касается того, почему он такой медленный, я не могу сказать, ты

Просто проверь это. Вы правы. :) Sylvain Rodrigue
Интересно, будут ли проблемы с совместимостью, если среда выполнения автоматически сгенерируетEquals переопределить для любой структуры, которая еще не определена:bool Equals(object other) { return StructComparer<thisType>.EqualsProc(ref this, other); }, гдеEqualsProc было статическим полем делегата в статическом классеStructComparer<thisType>? Такой подход позволил бы избежать необходимости использовать Reflection каждый раз, когда сравнивается объект, а также избежать шага бокса. supercat
10

когда это не нужно. Это просто сравнить значения по крупицам в случае, еслиstruct если это возможно. Однако, если какой-либо изstruct члены (или члены участников, любые потомки) переопределяютobject.Equals и предоставляют собственную реализацию, очевидно, она не может полагаться на побитовое сравнение для вычисления возвращаемого значения.

Причина медленная в том, что параметрEquals имеет типobject и типы значений должны быть упакованы, чтобы их можно было рассматривать какobject. Бокс включает в себя выделение памяти в куче и копирование памяти в этот тип значения.

Вы можете вручную обеспечить перегрузку дляEquals метод, который берет ваш собственныйstruct как параметр для предотвращения бокса:

public bool Equals(MyStruct obj) {
     return obj.i == i;
}
Sylvain: они правы. Как сказал Джон, если структура содержит ссылочные типы в качестве членов, она должна вызывать Equals для этих полей. Я обновил ответ, чтобы отразить это. Смысл, который я пытался подчеркнуть, заключается в том, что он не использует отражение, когда в этом нет необходимости (как в вашем примере). Mehrdad Afshari
В некоторых случаях он использует отражение. Если он обнаруживает, что может просто перенаправить результаты, он делает это, но если в полях есть ссылочные типы (или типы, содержащие ссылочные типы), он должен выполнить более болезненный процесс. Jon Skeet
Я должен сказать, я не понимаю, почему нельзя делать побитовое сравнение, когда есть ссылки. Если две ссылки указывают на один и тот же объект, когда указатели не будут в точности равны? snarf
Пока я писал это, я перечитал и обнаружил, что некоторые авторы .Net-структур (Cwalina, Abrams) просто подтверждают, что Equals использует рефлексию для типов значений. Но, может быть, только в Framework 2. Sylvain Rodrigue

Похожие вопросы