2016-11-24 3 views
7

Я создал пользовательскую структуру для представления суммы. Это в основном обертка вокруг decimal. У него есть оператор неявного преобразования, который возвращает его обратно в decimal.Почему Assert.AreEqual на пользовательской структуре с неявным оператором преобразования терпит неудачу?

В моем модульном тесте я утверждаю, что сумма равна первоначальному десятичному значению, но тест не выполняется.

[TestMethod] 
public void AmountAndDecimal_AreEqual() 
{ 
    Amount amount = 1.5M; 

    Assert.AreEqual(1.5M, amount); 
} 

Когда я использую Int, хотя (для которого я не создавал оператор преобразования), тест делает успеха.

[TestMethod] 
public void AmountAndInt_AreEqual() 
{ 
    Amount amount = 1; 

    Assert.AreEqual(1, amount); 
} 

Когда я наведу AreEqual, это показывает, что первый один решает

public static void AreEqual(object expected, object actual); 

и второй один ведет к

public static void AreEqual<T>(T expected, T actual); 

Похоже, что значение int1 неявно литой до Amount, а значение decimal1.5M - нет.

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

Когда я добавляю неявный листинг к int (что не имеет смысла), второй модульный тест также терпит неудачу. Поэтому добавление неявного оператора литья прерывает модульный тест.

У меня есть два вопроса:

  1. Что такое объяснение такого поведения?
  2. Как исправить структуру Amount, чтобы оба теста были успешными?

(я знаю, что я мог бы изменить тест, чтобы сделать явное преобразование, но если я не совсем есть, я не буду)

My Amount (просто-структуру минимальной реализации в показать проблему)

public struct Amount 
{ 
    private readonly decimal _value; 

    private Amount(decimal value) 
    { 
     _value = value; 
    } 

    public static implicit operator Amount(decimal value) 
    { 
     return new Amount(value); 
    } 

    public static implicit operator decimal(Amount amount) 
    { 
     return amount._value; 
    } 
} 
+1

Оба этих оператора являются «неявными». Итак, должно ли оно быть 'AreEqual ' или 'AreEqual '? – PetSerAl

+1

@PetSerAl является правильным. если вы используете 'AreEqual ' или 'AreEqual ' он пройдет. – Nkosi

ответ

6

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

Из-за неявного преобразования компилятор может выбрать Assert.AreEqual<decimal>(1.5M, amount); и Assert.AreEqual<Amount>(1.5M, amount); с равным значением. *

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

Поскольку нет перегрузки для выбора по умозаключению, не заходите в список для выбора наилучшего соответствия, и доступна только форма (object, object). Так это тот, кого выбрали.

С Assert.AreEqual(1, amount) тогда, поскольку существует неявное преобразование из int в Amount (через неявную int-> десятичную систему), но не неявное преобразование из Amount в int компилятор считает, что «очевидно, что они означают Assert.AreEqual<Amount>() здесь» †, и поэтому выбрал.

Вы можете явно выбрать перегрузку с Assert.AreEqual<Amount>() или Assert.AreEqual<decimal>(), но вы, вероятно, лучше сделать одну из ваших конверсий, «сужение» от который должен быть explicit, если вообще возможно, потому что эта особенность вашей структуры будет снова причинить тебе боль , (Ура для единичных тестов, обнаруживающих недостатки).


* Другой действительный выбор перегрузки выбрать Assert.AreEqual<object>, но он никогда не взял на умозаключения, потому что:

  1. Оба отвергнутые Перегрузки были рассмотрены еще лучше.
  2. Он всегда избит не-общей формой, которая принимает object в любом случае.

Как таковой, его можно вызвать только путем включения в код кода <object>.

† Компилятор рассматривает все сказанное как очевидное по смыслу или совершенно непонятное. Есть такие люди.

+0

Спасибо за ваш ответ. Я, мой случай, «неявный» возврат в «десятичный» не был действительно нужен, поэтому я изменил его на «явный». Интересно, однако, как я мог сам выяснить, почему вызывается AreEqual (объект, объект). Ошибок компилятора нет, но он просто не делает того, что я ожидал. Какие-нибудь советы по этому поводу? Можете ли вы как-нибудь увидеть какое-то дерево решений для компилятора? – comecme

+1

@comecme, если меня смущает выбор перегрузки, я сначала пишу некоторые методы, которые соответствуют сигнатуре (поэтому здесь я мог бы создать «AssertAreEqual» (объект x, объект y) »и« AssertAreEqual (T x, T y) '. Затем подтвердите, что я получаю такое же поведение (форма объекта берется), а затем удаляю тот, который выбрал, чтобы увидеть, что происходит:' CS0411. Аргументы типа для метода «AssertAreEqual (T, T)» не могут быть выведены из использование. Попробуйте явно указать аргументы типа. «Удивление, почему они не могут быть выведены, было бы большой подсказкой. Теперь, когда я иду явно указывать, у меня есть вопрос ... –

+0

... должен ли я явно указывать« »или явно скажем, ''? Ха! Я не знаю, так как же может возникнуть проблема с компилятором? Идентификатор проблемы. Большая проблема заключается в том, что я - свежая пара глаз, глядя на ваш код, не имея плана для него ум w что делает последний умственный прыжок легче сделать. Когда это собственный код, у него есть план, и может быть труднее понять, почему то, что очевидно для человека, который сделал этот план, не принадлежит компилятору. Единственное, что нужно сделать, это пойти и выпить чашечку кофе, прогуляться и вернуться к нему для более свежего взгляда. –

 Смежные вопросы

  • Нет связанных вопросов^_^