C#
StackOverflow

(a == 1 && a == 2 && a == 3)をちゃんと判定させる

周回遅れぎみだけどやってみた

元ネタ: Can (a ==1 && a== 2 && a==3) ever evaluate to true?
独自クラスを作って==演算子を実装するのはいいんだけど、だいたい「常にtrue」を返すだったので、ちゃんと値を持っているか確認してその結果を返すやつを作ってみた。

仕様

  • 複数の整数値を格納できるクラスを作成
  • ==演算子は「その比較値を格納していればtrueを返す」
  • 複数の値をセットしたり削除したりできるように+=で追加、-=で削除する機能も実装

実装したクラス

using System;
using System.Collections.Generic;
using System.Linq;

class MultiValueInt
{
    protected HashSet<int> values;

    //コンストラクタ
    public MultiValueInt() => values = new HashSet<int>();
    public MultiValueInt(int val) : this(new int[] { val }) { }
    public MultiValueInt(IEnumerable<int> vals) => values = new HashSet<int>(vals);

    //追加
    public static MultiValueInt operator +(MultiValueInt target, int val) { target.values.Add(val); return target; }
    public static MultiValueInt operator +(MultiValueInt target, IEnumerable<int> source) { source.Select(val => target.values.Add(val)); return target; }
    public static MultiValueInt operator +(MultiValueInt target, MultiValueInt source) { return target + source.values; }

    //削除
    public static MultiValueInt operator -(MultiValueInt target, int val) { target.values.Remove(val); return target; }
    public static MultiValueInt operator -(MultiValueInt target, IEnumerable<int> source) { source.Select(val => target.values.Remove(val)); return target; }
    public static MultiValueInt operator -(MultiValueInt target, MultiValueInt source) { return target - source.values; }

    //比較
    public static bool operator ==(MultiValueInt target, int val) => target.values.Contains(val);
    public static bool operator !=(MultiValueInt target, int val) => !target.values.Contains(val);

    //オーバーライドしないとなんか警告が出る
    public override bool Equals(object obj) { if (obj is MultiValueInt src) { return src.values.All(val => this.values.Contains(val)); } else return false; }
    public override int GetHashCode() => this.values.GetHashCode();
}

テストコード

static void Main()
{
    var a = new MultiValueInt(new int[] { 1, 2, 3 });
    Console.WriteLine($"(a==1 && a==2 && a==3) : {a == 1 && a == 2 && a == 3}");    //true
    Console.WriteLine($"(a==1 && a==2 && a!=3) : {a == 1 && a == 2 && a != 3}");    //false

    a -= 2;
    Console.WriteLine($"(a==1 && a==2 && a==3) : {a == 1 && a == 2 && a == 3}");    //false
    Console.WriteLine($"(a==1 && a!=2 && a==3) : {a == 1 && a != 2 && a == 3}");    //true
}

HashSet<T>について

あまり使われてるのを見かけたことがないけど、「その値をすでに使っているかどうかを知りたいだけ」という場合などには使いやすい。(数が多いとList<T>などより高速)
実質的には「Dictionaryのvalueがなくてkeyだけのやつ」なんだけど、valueに入れるものがなくてkeyだけをセットしてるDictionaryがあったらHashSet<T>に置き換えると記述がすっきりする。
「すでにセットしている値を追加」や「セットされてない値を削除」をしようとした場合は何も変化せず例外も投げないのでコードがシンプルになる。
「valueのないDictionary」だから格納順序は保証されない。