Do define operators on Value types that are logically a built in language type (such as System.Decimal)
Do provide operator-overloading methods only involving the class the methods are defined on.
Do use the names and signature conventions described in the CLS.
Don't be cute.
Operator overloading is useful in the cases where it is immediately obvious what the result of the operation would be. For example, it makes a lot of sense to be able to subtract one Time value from another and get a TimeSpan. However, it's not appropriate to use an or operator to union two database queries, or to use shift to write to a stream.
Do provide alternate signatures.
Not all languages support calling overloaded operators. For this reason, always include a secondary method with an appropriate domain-specific name that has equivalent functionality. For example:
class Time { TimeSpan operator -(Time t1, Time t2) { } TimeSpan Difference(Time t1, Time t2) { } }