Search code examples
c#operator-overloadingshort-circuiting

How to make multiplication operator (*) behave as short-circuit?


I have lots of computations, specially multiplication, where first part is sometimes zero and I don't want to evaluate second operand in that case. There are at least two short-circuit operators in C#: && and || which evaluate second operand only if necessary. I want to achieve similar behavior with multiplication operator.

In .net you can't overload && operator directly, but you can overload & and false operators, so you can use your extension points to change behavior of short-circuit operator. You can find more details at this article C# operator overloading: the ‘&&’ operator

Is there any means to achieve this or similar behavior for multiplication operator?

This is a pure syntax question, because implementation is very simple. Next method achieves exactly what I want in terms of functionality:

public static double ShortCircuitMultiply(double val, Func<double> anotherValue)
{
    var epsilon = 0.00000001;
    return Math.Abs(val) < epsilon ? 0 : val * anotherValue();
}

Note: this implementation is not full: in C# if you multiply 0.0 with Double.NaN or Double.NegativeInfinity or Double.PositiveInfinity, you'll get NaN, but in terms of ShortCircuitMultiply - only zero. Let's ignore this detail and it's really irrelevant in my domain.

So now if I call it as ShortCircuitMultiply(0.0, longOperation) where longOperation is Func<double>, last term won't be evaluated and result of an operation will be effectively zero.

The problem is, as I already stated, I would have lots of ShortCircuitMultiply calls and I want to make code more readable. I want code to be similar to 0.0 * longOperation() if that is possible.


Another note: I've tried to build wrapper on double and create implicit casting to double and also overload * operator. I understand, that this is probably redundant: I wanted to acheive readability, but trying to build yet another wrapper. Anyway, next code demonstrates my intent:

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);
    }
}

Now if I go with:

MyDouble zero = 0;

Console.WriteLine (zero * longOperation()); //longOperation is still Func<double>

I've receive:

cast to MyDouble            
called longOperation        <-- want to avoid this (it's printed from longOperation body)
cast to double             
cast to MyDouble
* operator call
cast to double   
0

But as you can see, longOperation is evaluated long before overloaded operator is called, and I can't substitute one of the parameters with Func or Expression to make it lazy.


Solution

  • The problem with your MyDouble wrapper class is that you use it by calling longOperation directly. As * is not short circuiting, it will be called directly.

    Instead, you could just make your wrapper accept a Func<double> as the second parameter instead of the double value itself. So it would work like the ShortCircuitMultiply function:

    public static MyDouble operator *(MyDouble left, Func<double> right)
    {
        return Math.Abs(left.value) < epsilon ? new MyDouble(0) : new MyDouble(left.value * right());
    }
    

    Then you would use it like this:

    MyDouble x = 0;
    Console.WriteLine(x * LongOperation);
    

    And even chaining works:

    MyDouble x = 5;
    Console.WriteLine(x * OperationReturingZero * LongOperation);
    

    Full example

    class Program
    {
        static void Main()
        {
            MyDouble x = 0;
            Console.WriteLine(x * LongOperation);
    
            MyDouble y = 5;
            Console.WriteLine(y * OperationReturningZero * LongOperation);
    
            Console.ReadLine();
        }
    
        private static double LongOperation()
        {
            Console.WriteLine("LongOperation");
            return 5;
        }
    
        private static double OperationReturningZero()
        {
            Console.WriteLine("OperationReturningZero");
            return 0;
        }
    }
    
    class MyDouble
    {
        private static double epsilon = 0.00000001;
        private double value;
    
        public MyDouble(double value)
        {
            this.value = value;
        }
    
        public static MyDouble operator *(MyDouble left, Func<double> right)
        {
            Console.WriteLine("* (MyDouble, Func<double>)");
            return Math.Abs(left.value) < epsilon ? new MyDouble(0) : new MyDouble(left.value * right());
        }
    
        public static MyDouble operator *(MyDouble left, MyDouble right)
        {
            Console.WriteLine("* (MyDouble, MyDouble)");
            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);
        }
    }
    

    Output:

    cast to MyDouble
    * (MyDouble, Func<double>)
    cast to double
    0
    cast to MyDouble
    * (MyDouble, Func<double>)
    OperationReturningZero
    * (MyDouble, Func<double>)
    cast to double
    0