知らずに落ちると抜け出せなくなるVBAの落とし穴です。
- Range変数同士が同一のセルなのかうまく判定できない
- セルをKeyとしたDictionaryがうまく機能しない
あたりにお悩みの方は、この落とし穴に落ちていないかご確認ください。
RangeオブジェクトにはIs演算子が使えない
Rangeオブジェクトには「取得ごとに違うオブジェクトを用意する」という性質があり、全く同じセルを指していたとしてもIsによる比較がFalseを返します。
Debug.Print Worksheets("データ") Is Worksheets("データ") ' ←当然True Debug.Print Range("A1") Is Range("A1") ' ← これがFalseになってしまう。
理由は定かではありませんが、A1セル、セル範囲A1:B2、A列など、
Rangeオブジェクトがセル数以上に無数に存在するためではないでしょうか。
このため、
- 2つのRangeオブジェクトが一致しているか
- あるRangeオブジェクトが対象セルを指しているか
あたりの判定を行うときに、Is演算子を使うことができません。
Rangeオブジェクトが同じセルを参照しているか判定する方法
この解決方法は単純で、Addressプロパティで比較・判定ができます。
Addressプロパティは第4引数ExternalをTrueにすることで、
シートとブックの指定もあるアドレスを返します。
これを使えば同一のセルであるかを判定することができます。
Debug.Print Range変数1.Address(External:=True) ' これで[テスト.xlsm]Sheet1!$A$1 のようなシート数式に使う形式が返ります。 ' 判定はこれでOK If Range変数1.Address(External:=True) = Range変数2.Address(External:=True) Then
これでブック・シート・セルアドレスのすべての一致を同時に判定できました。
なお、この記述が面倒なときは、第4引数であることとTrueは1でもいいことを利用し、
If Range変数1.Address(, , , 1) = Range変数2.Address(, , , 1) Then
こう書いてもいいと思います。
さらには、マクロの仕様上別シートを指していることはないということであれば、
If Range変数1.Address = Range変数2.Address Then
これで書くのも一つの手です。
ストレートな記述になっているので、何がやりたいかは一目瞭然ですしね。
どのコードを用いるかは、作るマクロの仕様や規模と相談してください。
DictionaryやCollectionのKeyとしても使用できない
こちらはレアケースかもしれませんが、Is演算子がFalseなのと同じ理屈で、
Dictionary(Collection)は同一のセルを同じKeyだと認識してくれません。
Dicセルリスト.Add Range("A1"), "" Debug.Print Dic.Exists(Range("A1")) ' ← これもFalse
この場合も、Addressプロパティの結果をKeyに渡して対応してください。