はじめに
if(a == 1 && a == 2 && a == 3)
を常にtrue
にしたい、という話が結構前にありました。
Can (a== 1 && a ==2 && a==3) ever evaluate to true?
これをC#で推奨されない方法で解決してみたいと思います。
問題の整理
さて、この問題ですべきことを見てみましょう。
-
if(a == 1 && a == 2 && a == 3)
を常にtrue
にしたい
まあ単純な話で、副作用を持つプロパティを使用すればいいのでは……と思いきや、単純にその方法で実装してしまっては、次のようなコードにおいて、問題文の条件を達成できません。
if(a == 1 && a == 2 && a == 3)
{
//何らかの処理
}
if(a == 1 && a == 2 && a == 3)
{
//ここを通らない
}
問題で与えられているのは、if(a == 1 && a == 2 && a == 3)
を常にtrue
にすることです。
問題解決への道筋
ということで今回は、以下のようなコードで条件を達成することを考えます。
using System;
namespace MyNamespace
{
class MyClass
{
static void Main()
{
var a = 1;
if(a == 1 && a == 2 && a == 3)
{
Console.WriteLine("Hello, world!");
}
if (a == 1 && a == 2 && a == 3)
{
Console.WriteLine("Hello, myself!");
}
}
}
}
演算子オーバーロードと文脈キーワード
文脈キーワード
文脈キーワードとは、特定の文脈でのみキーワードとなるキーワードのことです(正確には、contextual keywordといいます)。
たとえば、C#においてyield
は単独ではキーワードではありません。
ですが、後ろにbreak
やreturn
がつくと、yield break
やyield return
という一つのキーワードとして動作するようになります。
さて、ご存知の通り、C#においてvar
は文脈キーワードです。
以下のコードがコンパイル可能なことからもわかるでしょう。
namespace MyNamespace
{
class VarVariable
{
object var;
}
}
これはvar
という名前のフィールドを宣言しています。
そして、以下のコードもコンパイル可能です。
namespace MyNamespace
{
class VarClass
{
class var
{
}
static void Main()
{
var var;
}
}
}
わかりにくいのですが、Main()にあるvar
の型は型推論を行うためのvar
ではなく、VarClassで定義された型としてのclassのvar
です。
暗黙の型変換
さて、見かけ上何もしていない風に見せかけたいので、var
型の変数var
に1
を代入したいのですが、当然1
はそのままでは数値なので代入できません。
そのため、暗黙の型変換を定義します。
namespace MyNamespace
{
class VarClass
{
class var
{
public static implicit operator var(int i) => new var();
}
static void Main()
{
var a = 1;
}
}
}
これでvar
型の変数a
に、1
が型変換されて入るようになります。
等価のオーバーロード
次に、a
と数値
の==での比較をtrue
にする方法を考えましょう。
これは単純で、==演算子をオーバーロードするだけでOKです。が、!=演算子もオーバーロードする必要があります。
class var
{
public static implicit operator var(int i) => new var();
public static bool operator ==(var v, int i) => true;
public static bool operator !=(var v, int i) => false;
}
これで左辺にvar型、右辺にint型が来たときの==の動作を定義することができました。
最後にvar.classを隠蔽するためにvar.classを別ファイルに分けてあげれば完成です。
注釈
- 小文字で始まるクラスを作るのはやめよう
- ==と!=をオーバーロードしたときはちゃんとEquals()とかGetHashCode()とかToString()もオーバーロードしよう