白猫のメモ帳

JavaとかC#とかJavaScriptとかHTMLとか機械学習とか。

nullチェックしたらNullReferenceException

こんにちは。

秋ですね。
ようやく暑さも収まりましたが、体調が安定しません…。

なんだか謎掛けみたいなタイトルですが、
変な罠に引っかかりそうになったので備忘録。

なぞなぞ

nullチェックするようなロジックを書くべきではないとかいう話はまぁちょいちょいありますが、
個人的にはnull自体はそんなに嫌いではないです。

いやべつにNullableとかOptionalを否定する気はないですけど。
null条件演算子とかnull合体演算子とかもありますしね。

で、まぁこういうコードよく書きますよね。

var hoge = GetHoge();
if (hoge != null) 
{
    hoge.Fuga();
}

このコードでFugaの呼び出しの際にhogeがnullでNullReferenceExceptionが発生するのはどんなときでしょう?

なんかJSのクイズでたまに見る次のコードで「false」がアラートされるaを答えなさいみたいな感じがありますが。

if (a === a) {
    alert("true");
} else {
    alert("false");
}

正解はHogeの「==」「!=」を演算子オーバーロードをミスって書いたときです。
ちなみにJSの方の答えは「NaN」ですね。

演算子オーバロードって

演算子オーバーロードについては前に記事を書きましたので詳しくはこちらをどうぞ。
shironeko.hateblo.jp

たとえばプロパティを一つだけ持つクラスの場合、
こんな感じで演算子オーバーロードを書きたいですよね。

class Hoge
{
    public int Piyo { get; }

    public Hoge(int piyo)
    {
        this.Piyo = piyo;
    }

    public static bool operator ==(Hoge a, Hoge b)
    {
        return a.Piyo == b.Piyo;
    }

    public static bool operator !=(Hoge a, Hoge b)
    {
        return a.Piyo != b.Piyo;
    }
}

が、これをnullと比較すると第二引数にnullが入ってきて死ぬわけです。
演算子オーバーロードは左右を意識するので、null == ~って書くと第一引数側で死にます)

public static bool operator ==(Hoge a, Hoge b)
{
    if (a == null && b == null)
    {
        return true;
    }
    else if (a != null && b != null)
    {
        return a.Piyo == b.Piyo;
    } 
    else
    {
        return false;
    }
}

public static bool operator !=(Hoge a, Hoge b)
{
    return !(a == b);
}

でまぁこんな感じかなと思って書くと、再帰になってStackOverflowで死ぬわけです。
というわけでこうですね。

public static bool operator ==(Hoge a, Hoge b)
{
    if (a as object == null && b as object == null)
    {
        return true;
    }
    else if (a as object != null && b as object != null)
    {
        return a.Piyo == b.Piyo;
    } 
    else
    {
        return false;
    }
}

public static bool operator !=(Hoge a, Hoge b)
{
    return !(a == b);
}

この辺の書き方を間違えたりすると最初の例のようにnullチェックは通るのに実態はnullでしたみたいなことになるんです。
親クラスで演算子オーバーロードしてたりするとより沼にハマりやすくなります。

そういえば

しれっと順番を意識するのでとか書いたのですが、
二項演算子オーバーロードでは片側が自分の型であればもう片側は別の型でも問題ありません。

ということはですよ。
こんなふうに書いたらどうなるのっていう疑問がわきます。

class Hoge
{
    public static bool operator ==(Fuga a, Hoge b)
    {
        (略)
    }

    public static bool operator !=(Fuga a, Hoge b)
    {
        (略)
    }
}

class Fuga
{
    public static bool operator ==(Fuga a, Hoge b)
    {
        (略)
    }

    public static bool operator !=(Fuga a, Hoge b)
    {
        (略)
    }
}

結論としてはコンパイル通りませんでした。なんて普通なオチ…。

排他的論理和は^じゃなくて!=でよくない?

こんにちは。
ほんとに、ほんとーに毎日毎日暑いですね。
もうとろけてしまう。

で、なんだか偉い人に怒られてしまいそうなタイトルですが、
論理演算子を条件演算にしか使わないような我々には、
滅多に現れない排他的論理和をググりながら書くよりはいいんじゃないのってお話。

論理演算の種類

論理演算の種類はたくさんあるのですが、実際プログラミングで利用するものはある程度限られています。

論理積(AND)

条件Pと条件Qがあるとき、PとQの両方が真であることを論理積と呼びます。
(true、falseって書いたら読みづらかったので、○×にしますね。)

条件P 条件Q AND
× ×
× ×
× × ×

一般的にプログラミング言語では記号「&」を利用して、

p & q

のように記述します。

また、条件演算に利用する場合には、左から評価するとすれば、
条件Pがfalseの場合には条件Qの値にかかわらずfalseとなるため、短絡評価(ショートサーキット)することができます。
これは一般的にプログラミング言語では記号「&」を二つ重ねて、

p && q // pがfalseの場合、qの評価はしない

のように記述します。
今回の記事では深くはつっこみませんが、パフォーマンスの観点や副作用の観点から、一般的には短絡評価を使うことが多いです。

否定論理積(NAND)

条件Pと条件Qがあるとき、PとQの少なくとも片方が偽であることを否定論理積と呼びます。

条件P 条件Q NAND
×
×
×
× ×

論理積(AND)の反転であることから、一般的にプログラミング言語では否定「!」を利用して、

!(p && q)

のように記述します。

論理和(OR)

条件Pと条件Qがあるとき、PとQの少なくとも片方が真であることを論理和と呼びます。

条件P 条件Q AND
×
×
× × ×

一般的にプログラミング言語では記号「|」を利用して、

p | q

のように記述します。

また、論理積と同様に、条件Pがtrueの場合には条件Qの値にかかわらずtrueとなるため、短絡評価することができます。

p || q // pがtrueの場合、qの評価はしない

こういうことですね。

否定論理和(NOR)

条件Pと条件Qがあるとき、PとQの両方が偽であることを否定論理和と呼びます。

条件P 条件Q AND
×
× ×
× ×
× ×

論理和(OR)の反転であることから、一般的にプログラミング言語では否定「!」を利用して、

!(p || q)

のように記述します。

排他的論理和(XOR)

条件Pと条件Qがあるとき、PとQどちらか片方が真で、もう片方が偽であることを排他的論理和と呼びます。

条件P 条件Q AND
×
×
×
× × ×

一般的にプログラミング言語では記号「^」を利用して、

p ^ q

のように記述します。
Excelの式やVB系では「^」はべき乗の意味で利用されるので注意)

また、条件Pの値が決まっても条件Qの値が決まらないと判定できないため、短絡評価はありません。

定排他的論理和 (XNOR)

条件Pと条件Qがあるとき、PとQ双方が真または偽であることを否定排他的論理和と呼びます。

条件P 条件Q AND
× ×
× ×
× ×

一般的にプログラミング言語では記号「^」を利用して、

!(p ^ q)

のように記述…するんですかね?あんまりしない気もしますね。

XORとかXNORって使う?

要するにAND、OR、XORとその反転(否定)があるよって話なのですが、
せっかく演算子まで用意してくれてますけど、「^」ってコード上で見ることあんまりないですよね。

めったに使う場面がないのだから、使えるタイミングでは積極的に使っていこう!っていうのもいいのですが、
自分は分かっても他の人的には混乱するかも?って思うと使いにくいのですよね。

で、そもそもの話、XORの「PとQどちらか片方が真で、もう片方が偽である」
っていうのはPとQの結果が一致していない、つまり、

p != q

なんですよね。

同じように、XNORの「PとQ双方が真または偽である」
っていうのはPとQの結果が一致している、つまり、

p == q

ってこと。

なんかこっちのほうが見慣れてるからいいかなって思うのです。

まぁ、「ここは排他的論理和が正しくない?」とかいったらかっこいいのかもしれないですけどね。
ちなみに「排他的論理積」なんて言葉はないですよ。ですよー。

JavaScriptでメソッドをイベントバインドするとthisは何を指すの?

こんにちは。

ワールドカップが始まりましたね。
3時からとかだといっそ諦めがつくのですが、
0時くらいから始まる試合とかをつい見てしまって翌日ねむねむになる気がしてなりません。

さて、この前JavaScriptを書いていてふと思ったこと。
表題の通りです。

先に結論を書いてしまえば、イベントが発生したDOMになります。

JavaScriptのthisの指すものの種類はいろいろとあるのですが、
今回の目的はそれを解説することではないので、話題にする2つだけまず確認しましょう。

メソッドが指すthis

JavaScriptに限った話ではないですが、
わりとあいまいに使われている「関数」と「メソッド」は実際の挙動としては明確に区別されます。
Javaとかのオブジェクト指向言語だと関数はstaticメソッドとか呼ばれますね)

簡単にいえばメソッドとはオブジェクトに属する「メンバ関数」のことです。
なので、基本的にはオブジェクトに対する操作を定義した手続きなのですが、
オブジェクトのメンバにまったく関係しない、参照透過性のある関数を定義することができないわけではありません。

おっと、ちょっと話がそれましたね。
つまり、こんな感じのfunctionの定義はメソッドです。

var obj = {};
obj.method = function() {
    alert("hoge");
}

もしくはこんなのとか。

var obj = {
    method: function() {
        alert("hoge");
    }
};

で、このメソッドの中でthisが何を指すかというと、「自身が属しているオブジェクト」です。

var obj = {
    value: "hoge",
    method: function() {
        alert(this.value);
    }
};
obj.method();  // hoge

こんな感じですね。ほかの言語でも大体同じなんじゃないでしょうか。

イベントバインドした関数が指すthis

次にイベントバインドについてみていきましょう。

クライアントサイドのJavaScriptを考えた場合、画面初期表示時に様々な処理を行うほかに、
ユーザが「ある要素をクリックした」とか「マウスカーソルを動かした」とか「キーボードで何か入力した」
といった様々なイベントが発生します。

これらのイベントが発生した際に、なにかしらの処理をさせたい場合、
イベントに処理を「バインド」してあげる必要があります。

document.getElementById("hoge").addEventListener("click", function() {
    alert("hogeをクリックしたよ!");
});

jQuery的で書くとこんな感じ。

$("#hoge").on("click", function() {
    alert("hogeをクリックしたよ!");
});

で、この関数の中でthisが何を指すかというと、「イベントが発生したDOM」です。

<input type="text" id="hoge" value="fuga" />

document.getElementById("hoge").addEventListener("click", function() {
    alert(this.value);
});
// クリックするとfugaがアラートされる(inputクリックしたらアラートとかどういうこと)

メソッドをイベントバインドした場合のthis

やっと本題。

これら2つって実は共存することができます。
つまりどういうことかというと、

<input type="text" id="hoge" value="fuga" />

var obj = {
    value: "hoge",
    method: function() {
        alert(this.value);
    }
};
document.getElementById("hoge").addEventListener("click", obj.method);
// クリックするとfugaがアラートされる

この場合、「this.value」が指すのは

・「obj」のメンバ変数である「value」の値である「hoge
・イベントが発生したinputが持つ値「fuga」

どっちなのでしょうか。

正解は、イベントが発生したinputが持つ値「fuga」です。

じゃあこんな風にinputが持たない属性の「val」とかにしてみると使えるのかというと、
あくまでinputから「val」属性を探しに行って、結果は「undefined」になります。

var obj = {
    val: "hoge",
    method: function() {
        alert(this.val);
    }
};
document.getElementById("hoge").addEventListener("click", obj.method);
// クリックするとundefinedがアラートされる

オブジェクトのメンバを参照することはできないので残念ですね。
で終わってもいいのですが、せっかくなので回避方法を検討します。

var obj = {
    val: "hoge",
    method: function() {
        var self = this;
        return function() {
            alert(self.val);
        };
    }
};
document.getElementById("hoge").addEventListener("click", obj.method());
// クリックするとhogeがアラートされる

こんな感じでオブジェクトのthisを囲い込んだクロージャをバインドしたりするとなんとかなったりしますね。

使いどころがそんなにあるかは謎ですが、覚えておいて損はないかな。