У меня много вычислений, особенно умножение, где первая часть иногда равна нулю, и я не хочу оценивать второй операнд в этом случае. В С# есть как минимум два оператора короткого замыкания: &&
и ||
, которые оценивают второй операнд только в случае необходимости. Я хочу добиться аналогичного поведения с оператором умножения.
В .net вы не можете напрямую перегружать оператор &&
, но вы можете перегружать операторы &
и false
, чтобы вы могли использовать ваши точки расширения для изменения поведения коротких сообщений, оператор цепи. Вы можете найти более подробную информацию в этой статье Перегрузка оператора С#: '& & Оператор
Есть ли какие-либо средства для достижения этого или подобного поведения для оператора умножения?
Это чисто синтаксический вопрос, потому что реализация очень проста. Следующий метод обеспечивает именно то, что я хочу с точки зрения функциональности:
public static double ShortCircuitMultiply(double val, Func<double> anotherValue)
{
var epsilon = 0.00000001;
return Math.Abs(val) < epsilon ? 0 : val * anotherValue();
}
Примечание: эта реализация не заполнена: в С#, если вы умножаете 0.0
на Double.NaN
или Double.NegativeInfinity
или Double.PositiveInfinity
, вы получите NaN
, но в терминах ShortCircuitMultiply
- только ноль. Пусть игнорируют эту деталь, и это действительно неуместно в моем домене.
Итак, теперь, если я назову его ShortCircuitMultiply(0.0, longOperation)
, где longOperation
- Func<double>
, последний член не будет оценен, а результат операции будет фактически равен нулю.
Проблема в том, что, как я уже сказал, у меня было бы много вызовов ShortCircuitMultiply
, и я хочу сделать код более читаемым. Я хочу, чтобы код был похож на 0.0 * longOperation()
, если это возможно.
Еще одно замечание: я попытался создать оболочку на double
и создать неявное литье для двойного, а также для перегрузки *
. Я понимаю, что это, вероятно, избыточно: я хотел получить читаемость, но пытаюсь создать еще одну оболочку. В любом случае следующий код демонстрирует мое намерение:
class MyDouble
{
double value;
public MyDouble(double value)
{
this.value = value;
}
public static MyDouble operator *(MyDouble left, MyDouble right)
{
Console.WriteLine ("* operator call");
return new MyDouble(left.value * right.value);
}
public static implicit operator double(MyDouble myDouble)
{
Console.WriteLine ("cast to double");
return myDouble.value;
}
public static implicit operator MyDouble(double value)
{
Console.WriteLine ("cast to MyDouble");
return new MyDouble(value);
}
}
Теперь, если я иду:
MyDouble zero = 0;
Console.WriteLine (zero * longOperation()); //longOperation is still Func<double>
Получаю:
cast to MyDouble
called longOperation <-- want to avoid this (it printed from longOperation body)
cast to double
cast to MyDouble
* operator call
cast to double
0
Но, как вы можете видеть, longOperation
вычисляется задолго до того, как вызывается перегруженный оператор, и я не могу заменить один из параметров Func
или Expression
, чтобы сделать его ленивым.