Namespace: `MiscUtil`

Types involved: `Operator, Operator<T>`

Note: these classes were written by Marc Gravell. I claim no credit for their brilliance! Jon.

The `Operator`

class provides high-performance support for the
common operators (+, -, *, etc), but using generics - meaning it can for for *any*
type with suitable operators defined. For a full discussion of why this isn't
provided by default, see the generic operators discussion page.

With regular C# you might use the operators, for instance:

int c = a + b;

There is no native equivalent for generics; however, the `Operator`

class provides an alternative:

T c = Operator.Add(a,b);

The `Add`

method is actually a generic method:

public static T Add<T>(T a, T b)

{

...

}

{

...

}

The compiler is able to use type inference to work out the type of `T`

automatically.
The system uses the `Expression`

class from .NET 3.5, which uses standard operators
declared on types, as well as IL-level operators (such as `Int32`

or `Single`

)
and `Nullable<T>`

- making it very versatile.

A brief aside for `Nullable<T>`

; recall that the rules for lifted operators
with `Nullable<T>`

mean that the result of a lifted operator with a null
operand is generally null. You should give consideration to how you want to treat nulls,
and code accordingly. Fortunately, this is simple, and the JIT-compiler does a good job of
removing unnecessary defensive code.

Also; commonly code needs to consider zero - particularly as the seed for sum operations;
however, `default(T)`

is *not* zero if `T`

is nullable - it is
null. To help with this, an `Operator<T>.Zero`

static property is provided,
which will be the true zero for `T`

.

The two following examples illustrate most of the key points for working with the `Operator`

class; they are heavily simplified (but generic) implementations of the `Sum()`

and
`Average()`

methods from LINQ.

First we'll consider `Add()`

; this discards any null values found and returns the
sum of the non-null values; if no (non-null) values are found, zero is returned in all cases.
This can be implemented very simply:

public static T Sum<T>(this IEnumerable<T> source)

{

T sum = Operator<T>.Zero;

foreach (T value in source)

{

if (value != null)

{

sum = Operator.Add(sum, value);

}

}

return sum;

}

{

T sum = Operator<T>.Zero;

foreach (T value in source)

{

if (value != null)

{

sum = Operator.Add(sum, value);

}

}

return sum;

}

We start at zero, and simply accumulate the values. If called with a non-nullable
struct type (`Int32`

for example), then the "if" test will be removed by the JIT,
simply calling the "true" portion (since such a value can never be null).

As a slightly more involved example, we'll consider `Average()`

; like `Sum()`

this
also discards any null values, and returns the mean average (sum divided by the number of
non-null values). Since an integer division (`T`

divided by `Int32`

) is a
very common requirement, an additional `Operator`

method is provided for convenience.

Also, note that the behaviour for the empty-case (where no non-null values are found)
varies: if `T`

is nullable, it returns null; otherwise it throws an exception.

public static T Average<T>(this IEnumerable<T> source)

{

int count = 0;

T sum = Operator<T>.Zero;

foreach (T value in source)

{

if (value != null)

{

sum = Operator.Add(sum, value);

count++;

}

}

if (count == 0)

{

sum = default(T);

if (sum != null)

{

throw new InvalidOperationException();

}

return sum;

}

else

{

return Operator.DivideInt32(sum, count);

}

}

{

int count = 0;

T sum = Operator<T>.Zero;

foreach (T value in source)

{

if (value != null)

{

sum = Operator.Add(sum, value);

count++;

}

}

if (count == 0)

{

sum = default(T);

if (sum != null)

{

throw new InvalidOperationException();

}

return sum;

}

else

{

return Operator.DivideInt32(sum, count);

}

}

As per the expected behaviour, this starts similar to `Sum()`

, but also tracking the
count of the values. If we have a non-zero count, we use `DivideInt32()`

to obtain the
average. Otherwise we use `default(T)`

; if this is null, then `T`

is nullable
and we can return this value (null); if not, throw an exception.

While these examples illustrate the ease of use, you are not limited to these operations.

All of the most common operators are available:

`public static T Add<T>(T value1, T value2)`

`public static T Subtract<T>(T value1, T value2)`

`public static T Multiply<T>(T value1, T value2)`

`public static T Divide<T>(T value1, T value2)`

`public static T DivideInt32<T>(T value, int divisor)`

`public static T Negate<T>(T value)`

`public static T Zero { get; }`

(Member of`Operator<T>`

)

`Comparer<T>`

and `EqualityComparer<T>`

):`public static bool Equal<T>(T value1, T value2)`

`public static bool NotEqual<T>(T value1, T value2)`

`public static bool GreaterThan<T>(T value1, T value2)`

`public static bool LessThan<T>(T value1, T value2)`

`public static bool GreaterThanOrEqual<T>(T value1, T value2)`

`public static bool LessThanOrEqual<T>(T value1, T value2)`

`public static T Not<T>(T value)`

`public static T Or<T>(T value1, T value2)`

`public static T And<T>(T value1, T value2)`

`public static T Xor<T>(T value1, T value2)`

`public static TTo Convert<TFrom, TTo>(TFrom value)`

If a critical operator has been missed (and can be sensibly defined in generic terms) then please let me know.

For advanced scenarios, there is also limited support for operators with very different operand types
(think `DateTime + Timespan`

=> `DateTime`

).
Sometimes you can design around the need to use them, but a limited subset are provided for convenience:

`public static TArg1 AddAlternative`

(TArg1 value1, TArg2 value2) `public static TArg1 SubtractAlternative`

(TArg1 value1, TArg2 value2) `public static TArg1 MultiplyAlternative`

(TArg1 value1, TArg2 value2) `public static TArg1 DivideAlternative`

(TArg1 value1, TArg2 value2)

The main slight weakness here is that is cannot provide compile-time safety; it won't
attempt to resolve the operators until runtime. In reality this is rarely an issue, and
is comparable to saying that `Comparer<T>.Default`

is unreliable
because `T`

might not implement `IComparable<T>`

(etc).
It is true, but then: you aren't likely to try and call `Sum()`

(etc) on such a type.

It doesn't support scenarios where the return is unrelated to either operand - i.e.
`TReturn Operator(TArg1, TArg3) {...}`

. Again, this is unlikely to be an
issue in reality; if somebody has a genuine use-case for this, please let me know!

Back to the main MiscUtil page.