Предположим, у нас есть следующий тип:
struct MyNullable<T> where T : struct
{
T Value;
public bool HasValue;
public MyNullable(T value)
{
this.Value = value;
this.HasValue = true;
}
public static implicit operator T(MyNullable<T> value)
{
return value.HasValue ? value.Value : default(T);
}
}
И попробуйте скомпилировать следующий фрагмент кода:
MyNullable<int> i1 = new MyNullable<int>(1);
MyNullable<int> i2 = new MyNullable<int>(2);
int i = i1 + i2;
Этот фрагмент скомпилирован хорошо и без ошибок. i1 и i2 приводится к целому числу и вычисляется сложение.
Но если у нас есть следующий тип:
struct Money
{
double Amount;
CurrencyCodes Currency; /*enum CurrencyCode { ... } */
public Money(double amount, CurrencyCodes currency)
{
Amount = amount;
Currency = currency;
}
public static Money operator + (Money x, Money y)
{
if (x.Currency != y.Currency)
// Suppose we implemented method ConvertTo
y = y.ConvertTo(x.Currency);
return new Money(x.Amount + y.Amount, x.Currency);
}
}
Попробуйте скомпилировать другой фрагмент кода:
MyNullable<Money> m1 =
new MyNullable<Money>(new Money(10, CurrenciesCode.USD));
MyNullable<Money> m2 =
new MyNullable<Money>(new Money(20, CurrenciesCode.USD));
Money m3 = m1 + m2;
А теперь вопрос, почему компилятор генерирует «ошибка CS0019: оператор '+' не может применяться к операндам типа 'MyNullable <Money>' и 'MyNullable <Money>'»?





Это интересный вопрос ... он работает, например, с Decimal, но не с TimeSpan, которые оба являются правильными типами .NET (в отличие от float и т. д., Которые являются примитивами), и оба имеют оператор +. Любопытный!
Конечно, руку можно крутить:
Money m3 = (Money)m1 + (Money)m2;
И если вы просто используете Nullable<T>, он, конечно, будет работать бесплатно - плюс вы получите поддержку компилятора + времени выполнения (бокса). Есть ли причина не использовать здесь Nullable<T>?
Я посмотрю на спецификацию; тем временем вы можете подумать о переводе оператора на MyNullable<T>; с обычным Nullable<T> компилятор C# предоставляет "расширенные" операторы для тех, которые поддерживаются типом, но вы не можете сделать это самостоятельно. Лучшее, что вы можете сделать, - это предложить все очевидные из них и надеяться, что тип их поддерживает ;-p Чтобы получить доступ к операторам с обобщенными типами, см. здесь, доступный для бесплатной загрузки здесь.
Обратите внимание, что вы, вероятно, захотите применить соответствующие «повышенные» проверки, т. Е.
x + y => (x.HasValue && y.HasValue)
? new MyNullable<T>(x.Value + y.Value)
: new MyNullable<T>();
Обновлять
Другая обработка выглядит как относящаяся к 14.7.4 (ECMA 334 v4) «Оператор сложения», где он предопределен для диапазона типов в том числе decimal (так что это был плохой тест для меня), поскольку в 14.2.4 ( то же самое) «Разрешение перегрузки бинарных операторов», предварительно определенные операторы заслуживают особого упоминания. Однако я не претендую на то, чтобы понять это полностью.
@lassevk - Nullable <Money> отлично работает - просто результат m1 + m2 равен Nullable <Money>, а не Money
MyNullable делает все наоборот. Значение типа Nullable неявно недоступно. MyNullable пытается этого добиться.
т.е. «Деньги? m3 = m1 + m2;» - компилируется и запускается с результатом 30, как и ожидалось
@AnthonyWJones - и не зря - что должно возвращать неявное приведение, если оно не имеет значения? Явное приведение (.Value) взрывается (разумно). В противном случае есть GetValueOrDefault ().
@AnthonyWJones - с комбинацией поднятых операторов (компилятор) и специальных правил бокса (время выполнения) и специальных общих правил (оба) - я не думаю, что вы когда-нибудь сможете сделать MyNullable <T> что близко к Обнуляемый <T>. Это действительно особенный, уникальный случай.
Хм, ладно, должно быть, я споткнулся с кодом или, возможно, не прочитал сообщение об ошибке.
@Marc: Да, это была моя точка зрения, Nullable не имеет неявного оператора T, поэтому происходит другая магия. Джон хорошо разбирается в разделе 4.3.3 своей книги.
Марк, ты абсолютно прав в том случае, если MyNullable создан только для неявного приведения к T. Потому что такое же выражение с System.Nullable выглядит так: Money? m1, m2; Деньги? m3 = m1.GetValueOrDefault () + m2.GetValueOrDefault (); Удаление вызова GetValueOrDefault () - это мой особый трюк с DSL.
Марк находится в правильных строках - это раздел 7.2.4 в спецификации C# 3.0 - Разрешение перегрузки двоичного оператора.
В основном шаги следующие:
MyNullable<Money>.MyNullable<T> не перегружает +.MyNullable<int> + MyNullable<int>, это работает из-за неявного преобразования каждого аргумента в int, но когда мы выполняем MyNullable<Money> + MyNullable<Money>, работает не, потому что Money + Money не входит в набор операторов-кандидатов.
К сожалению, Nullable <Money> тоже не работает, мне кажется, что встроенные примитивы каким-то образом обрабатываются особым образом, хотя я не знаю почему. Я искал определяемые пользователем неявные преобразования в спецификации C#, но, боюсь, я потерялся в технической неразберихе ... Надеюсь, вам это покажется странным ...