When does != true != !true?
Tags: csharp, dotnet
Published: 2023-12-13 10:56:33

This is a rather contrived tongue-in-cheek edge case, and super nerdy... but I have some code that does this:

```csharp
Console.WriteLine(liar == true); // True
Console.WriteLine(liar != true); // True
Console.WriteLine(!liar); // False
```

## WHY!?!

Partly because I'm a contrarian, but also to highlight something that we might take for granted in C#.

@leekelleher wrote [a great article about his take on coding standards](https://dev.to/leekelleher/lees-opinions-on-umbraco-coding-standards-4c7n) where he compares two different ways of doing the same thing, specifically this:

```csharp
if(!string.IsNullOrWhiteSpace(input)) { 
  //... 
}

// or

if(string.IsNullOrWhiteSpace(input) == false) { 
  //... 
}
```

Lee points out that functionally (and even in IL) these two lines of code are exactly the same (and for `string.IsNullOrWhiteSpace()` that will almost certainly always be the case), but they don't _have_ to be the same...

Logically these lines of code are doing something different, which shouldn't be a surprise because they _are_ different.

If we do away with the method being invoked, which returns a `bool`, and the `if` then we can focus a bit on the logic.

```csharp

var x = !a; // x IS NOT a.

var x = a == false; // x IS (a EQUALS false).

```

That first line is a logical negation. The second line is an equality comparison against a (static) `false` value. These are using different operators, and in C# operators are implemented explicitly and independently and can be overridden.

What do I mean by that? let's take a look at some code from my (very naughty) Liar struct.

## Jason's Evil Liar Struct
BTW, don't do this 🙈.

```csharp
public struct Liar(bool isTrue)
{
    private readonly bool _true = isTrue;

    public static bool operator ==(Liar left, bool right)
    {
        return left._true == right;
    }

    public static bool operator !=(Liar left, bool right)
    {
        return left._true == right; // 😈
    }
}
```

Here I'm defining what should happen when someone compares my Liar struct to a `bool`. You can, of course, spot the deliberate mistake.

The point is, `!=` and `==` invoke different methods, i.e. `!=` is not simply a logical negation of `==`. This means that `a != b` is not doing the same as `!(a == b)` and vice versa.

So what about the logical negation operator? You'd be forgiven for thinking that `a` _has_ to be a bool for `!a` to work, but that's not the case. It could be anything that has the implicit operator for a bool defined (or the true/false operators). That's right JavaScript fans, you can make truthy/falsey types in C# 😬.

Here's the code for implicit operator that lets me use my Liar struct as if it's a bool:

```csharp
public static implicit operator bool(Liar liar)
{
  return liar._true;
}
```

Now we can do this:

```csharp
Liar liar = new(true);

if (liar)
{
  //
}
```

We can also add an implicit operator so that we don't even need to create an instance of `Liar`, and can just assign true to it:

```csharp
public static implicit operator Liar(bool isTrue)
{
  return new(isTrue);
}
```

Now we can do this:

```csharp
Liar liar = true;
```

## Yeah, but this never happens in the _real world_

Unless someone does something silly... and people _never_ do silly things... 

Though that's not really my point, there are a few other things I'd like you to take away from this:

### 1. Code that looks like it's doing something different, _is_ doing something different

Two lines of code that look like they're doing something different almost always _are_ doing something different, even if the outcome is functionally the same.

Sure, in the real world the difference between  `!string.IsNullOrWhiteSpace(input)` and `string.IsNullOrWhiteSpace(input) == false` is largely academic, so  you can choose what works for you. **But** that's not necessarily going to be the case all the time, and being aware of the difference here might just save you a few hours of debugging one day.

### 2. In C# _we_ can choose how operators work
At least with our own code, it's called operator overloading. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading

I've known some C# devs get confused by equality in JavaScript with `==`, `===`, `!=`,`!==` and truthy/falsely logic like `!someString`. In many ways this is actually a lot simpler than C# because it is what it is, and we can't change it. 

In C# all bets are off - you have complete control in defining how operators work with the classes/structs etc. that you define.

This is actually a super powerful feature of C# and I love it when library authors implement it to improve the developer experience, like this example:

```csharp
var ratio = new(16,9);
if(ratio == "16:9")) //😙👌
```

### 3. You can write operators to compare pretty much anything!

My `Liar` is just a wrapped `bool` to make a silly example, but we can have any logic inside these operators and use them for more complicated types. 

There's also no limit on what types you can add operators for, here I've just added code to compare `Liar` with `bool` but we can write code to compare our types to anything.

## Thanks for nerding out with me...

If you've not had a go at overloading operators in your own types before, why not give it a try?

I'm sure you've got classes that would be useful if they could be compared against a `string` value, like the ratio example above, or an `int` or whatever.

---
Here's the full example `Liar struct`

```csharp
public readonly struct Liar(bool isTrue) : IEquatable<bool>
{
    private readonly bool _true = isTrue;

    public static bool operator ==(Liar left, bool right)
    {
        return left._true == right;
    }

    public static bool operator !=(Liar left, bool right)
    {
        return left._true == right; // 😈
    }

    public static implicit operator bool(Liar liar)
    {
        return liar._true;
    }

    public static implicit operator Liar(bool isTrue)
    {
        return new(isTrue);
    }

    public override bool Equals(object obj)
    {
        return obj is Liar liar && liar._true == _true;
    }

    public override int GetHashCode()
    {
        return _true.GetHashCode();
    }

    public bool Equals(bool other)
    {
        return _true == other;
    }
}
```