Назначение C # в конструкторе члену ... не меняет его - PullRequest
0 голосов
/ 26 февраля 2019

У меня есть простой класс, предназначенный для хранения масштабированных целочисленных значений с использованием переменных-членов «scaled_value» (long) с «scale_factor».

У меня есть конструктор, который заполняет новый экземпляр класса десятичным значением (хотя я думаю, что тип значения не имеет значения).Назначение слота "scaled_value" появляется ... не произойдет.Я вставил явное присвоение константы 1 для него.Ниже приведен сбой Debug.Assert ... и значение scaled_value равно нулю.

На разрыве подтверждения в ближайшем окне я могу проверить / установить с помощью присваивания / проверки "scale_factor";он меняется, как я его установил.

Я могу проверить "scaled_value".Это всегда ноль.Я могу напечатать для него присвоение, которое выполняет непосредственное окно, но его значение не меняется.

Я использую Visual Studio 2017 с C # 2017.

Что волшебного в этом слоте?

public class ScaledLong : Base // handles scaled-by-power-of-ten long numbers
                  // intended to support equivalent of fast decimal arithmetic while hiding scale factors from user
   {
  public long scaled_value; // up to log10_MaxLong digits of decimal precision
  public sbyte scale_factor;  // power of ten representing location of decimal point range -21..+21.  Set by constructor AND NEVER CHANGED.
  public byte byte_size;  // holds size of value in underlying memory array
  string format_string;

  <other constructors with same arguments except last value type>

  public ScaledLong(sbyte sf, byte size, string format, decimal initial_value)
  {
     scale_factor = sf;
     byte_size = size;
     format_string = format;

     decimal temp;
     sbyte exponent;
     {  // rip exponent out of decimal value leaving behind an integer;
    _decimal_structure.value = initial_value;
    exponent = (sbyte)_decimal_structure.Exponent;
    _decimal_structure.Exponent = 0;  // now decimal value is integral
    temp = _decimal_structure.value;
     }
     sbyte sfDelta = (sbyte)(sf - exponent);
     if (sfDelta >= 0)
     {  // sfDelta > 0
    this.scaled_value = 1;
    Debug.Assert(scaled_value == 1);
    scaled_value = (long)Math.Truncate(temp * DecimalTenToPower[sfDelta]);
     }
     else
     {
    temp = Math.Truncate(temp / DecimalHalfTenToPower[-sfDelta]);
    temp += (temp % 2); /// this can overflow for value at very top of range, not worth fixing; note: this works for both + and- numbers (?)
    scaled_value = (long)(temp / 2); // final result
     }

  }

1 Ответ

0 голосов
/ 27 февраля 2019

Самые большие загадки часто имеют самые глупые основания.Это урок непреднамеренных побочных эффектов.

Я обнаружил это, подумав о том, как удивительно, как член может быть изменен неожиданно.Я нашел решение до того, как прочитал комментарий @mjwills, но он определенно нюхал правильную вещь.

Что я упустил (конечно!), Так это то, что я только что написал код метода ToString () для класса... это не было отлажено.Почему я оставил это?Потому что это, очевидно, не может ни на что повлиять, поэтому это не может быть частью проблемы.

Bzzzzt!она использовала переменную-член как блокнот и обнуляла ее (есть побочный эффект);это было явно непреднамеренно.

Когда это означает, что когда код просто запускается, ToString () не вызывается, и переменная-член DOES корректно изменяется.(У меня даже были модульные тесты для процедуры «Set», которые проверяли все это, и они работали).

Но когда вы отлаживаете… отладчик может (и делал в этом случае) показывать локальные переменные,Для этого он, очевидно, вызовет ToString (), чтобы получить хорошее отображаемое значение.Таким образом, один шаг заставил ToSTring () быть вызванным, и его ошибочное присвоение переменной нуля обнуляло слот после каждого вызова шага.

Так что это не был сеттер, который меня укусил.Это было возможно добытчиком.(Где ключевое слово PURE FORTRAN, когда оно вам нужно?)

Эйнштейн ненавидел жуткие действия на расстоянии.Программисты ненавидят жуткие побочные эффекты на расстоянии.

Немного удивляет идея отладчика, вызывающего ToString () для класса, конструктор которого еще не завершен.Каким утверждениям о состоянии класса может доверять ToString, если конструктор не выполнен?Я думаю, что MS отладчик должен быть исправлен.При этом я бы потратил время на отладку ToString вместо того, чтобы гоняться за этим.

Спасибо, что согласились с моим вопросом.Это дало мне ответ.

...