10

私はたまにしか VBA を使用しませんが、VBA に戻るたびに、次のようなバリエーションに悩まされます。

スプレッドシートで作業しているセルを追跡するために使用するRangeオブジェクトがあります。currentCellこれを更新して別のセルを指すようにすると、次のように記述します。

currentCell = currentCell.Offset(ColumnOffset:=1)

問題は、キーワードを忘れてしまったことです。そのSetため、上記の行が実際に行うことは、Rangeオブジェクトのデフォルト プロパティを使用することです。

currentCell.Value = currentCell.Offset(ColumnOffset:=1).Value

したがって、現在のセルの内容は新しいセルの内容によって上書きされ、currentCell変数は新しいセルを指すように変更されていません。同じ間違いを 100 回も繰り返していることに気づき、怒りでいっぱいになります。 .

「今日 Set を使うのを覚えていましたか?」というポストイットをモニターに貼るよりも良い答えはおそらくないでしょうが、誰かが私を助けるために何か提案があれば、聞いていただければ幸いです. 特に:

  • デフォルトのプロパティを暗黙的に使用するときに警告をオンにする方法はありますか? 私は意図的にこのように使用したことはありませんRange.Value
  • 「これはスプレッドシートからの読み取りにのみ使用する必要があります」として変数をマークするための良い方法はありますか? 私が書いたほとんどのコードでは、ほとんどすべての変数がデータを収集するためのものであり、何かがこのようにセルを誤って編集し始めた場合に警告を受け取ると便利です。
4

6 に答える 6

6

範囲オブジェクトを何年も使用しているにもかかわらず、この問題が発生したことはないと思うので、問題がどのように発生しているかを理解するのに少し時間がかかりました。いろいろと考えた結果、セルを操作するときは常に次のことを行っていることに気付きました。常にoffsetorcells関数を使用Setしています。現在の変数を再定義するために使用することはめったにありません。

スプレッドシートを通過するために反復しているループがある場合、次のようなことをするかもしれません

Dim startRng as Range
Set startRng = range("A1")

for i = 1 to 100
   startRng.offset(i,0).value = i
   startRng.offset(i,1).value = startRng.offset(i,0).value
next i

また

for i = 1 to 100
    cells(i,0).value = i
    cells(i,1).value = cells(i,0).value
next i

これらの表記のいずれも、範囲オブジェクトを使用する必要がほとんどないことを意味しSetます。ほとんどの場合、これは (あったとしても) 1 回発生し、反復または参照する範囲内の最初のセルを示します。

offsetまた、a を指定しているため、が何であるかが非常に明確ですrow/column。これにより、コード内で何が起こっているかが非常にわかりやすくなり、単一のセルを参照するため追跡が容易になります。currentCell Rangeオブジェクトを更新した最後の 3 つの場所を追跡してさかのぼる必要はありません。

この種のスタイルを使用したコーディング スタイルを採用すると、これらのエラーのほとんどすべてが解消されます。私は、VBA をコーディングして何年にもわたって同様のエラーを犯したことを覚えていないと言うとき、私は非常に深刻です - 私は自分のコードでoffsetおよびcells関数を継続的に使用しています (これらの例ではループですが、コード内の他のすべての例では同様の方法を使用しています)。範囲を新しい範囲に設定するのではなく。副作用は、コードで範囲を設定する場合、ほとんど常にDimステートメントの直後であり、はるかに明確であることです。

于 2012-09-09T21:55:11.843 に答える
4

何をするにしても、ポストイットが必要になると思います。結局のところ、範囲オブジェクトの値を別のセルの値に設定することは、完全に有効で一般的なことです。コードは、あなたが要求したこと以外のことをしたいということを知る方法がありません。

更新の前後に範囲オブジェクトのアドレスをチェックして、それが異なることを確認することもできますが、それを覚えている場合は、単にsetキーワードを使用してオブジェクトを意図したとおりに更新する方がよいでしょう。

この問題に激怒して質問を投稿したので、次に VBA にアクセスするまでにどれだけの時間が経過したとしても、二度とこの問題を忘れることはないと思います。結局、ポストイットは必要ないかもしれません。

于 2012-09-09T21:43:13.897 に答える
3

「今日 Set を使うのを覚えていましたか?」というポストイットをモニターに貼るよりも良い答えはおそらくないでしょうが、誰かが私を助けるために何か提案があれば、聞いていただければ幸いです. 特に:

ポストイットの文言を少し変更します

「Option Explicit とエラー処理の使用を忘れていませんか?」

そうでなければ、これ以上の方法はないと信じてください!そうは言っても、「セットを使う」ということは、あなたの心配が最も少ないことを確認したいと思います。あなたの一番の心配事は「良いコードを書くこと」です。それはすべて練習によってもたらされます。

すべての初心者への私のアドバイス。決して仮定しないでください!たとえば.Value、範囲のデフォルト プロパティは

Range("A1") = "Blah" 

正しい。しかし、それでもそれを使用することは避けてください。

  1. 常に変数を完全修飾する
  2. 常に明示的なオプションを使用
  3. 常にエラー処理を使用する

たとえば、これは機能します。

Option Explicit

Sub Sample()
    Dim ws As Worksheet
    Dim rng As Range

    On Error GoTo Whoa

    Set ws = ThisWorkbook.Sheets("Sheet1")
    Set rng = ws.Range("A1")
    rng.Value = "Blah"

    Exit Sub
Whoa:
    MsgBox Err.Description
End Sub

それでは、Set コマンドを使用せずに上記のコードを試してみましょう。以下のコードを試してください。何が起こるのですか?

Option Explicit

Sub Sample()
    Dim ws As Worksheet
    Dim rng As Range

    On Error GoTo Whoa

    ws = ThisWorkbook.Sheets("Sheet1")
    rng = ws.Range("A1")
    rng.Value = "Blah"

    Exit Sub
Whoa:
    MsgBox Err.Description
End Sub

読むことをお勧めします。

トピック: 「Err」するのは人間です

リンク: http://siddharthrout.wordpress.com/2011/08/01/to-err-is-human/

于 2012-09-10T06:32:50.590 に答える
3

デフォルトのプロパティを暗黙的に使用するときに警告をオンにする方法はありますか?

いいえ。

「これはスプレッドシートからの読み取りにのみ使用する必要があります」として変数をマークするための良い方法はありますか?

Making Wrong Code Look Wrongのように、独自の変数命名規則を作成することもできますが、それでも独自のコードを視覚的にチェックする必要があり、コンパイラはそれを支援しません。だからあまり頼りたくない。

currentCellより良いオプションは、完全に使用して繰り返し再定義する必要性を回避することです.Offset

代わりに、関心のある範囲全体を Variant 配列に読み取り、その配列で作業を行い、変更が完了したらシートに戻します。

Dim i As Long
Dim j As Long
Dim v As Variant
Dim r As Range

Set r = Range("A1:D5") 'or whatever

v = r.Value 'pull from sheet

For i = 1 To UBound(v, 1)
    For j = 1 To UBound(v, 2)
        'code to modify or utilise element v(i,j) goes here
    Next j
Next i

r.Value = v 'slap v back onto sheet (if you modified it)

ほら。デフォルトのプロパティや、混同される可能性のあるものは使用しないでください。おまけとして、これによりコードの実行が高速化されます。

于 2012-09-10T06:50:17.860 に答える
1

エンダーランドは、複数のセルをループで処理するという基本的な解決策を強調したと思います。さらに詳しく説明すると、For Next ループを使用してセルを循環させることをお勧めします。おそらく、私が書く最も一般的なコードは次のようなものです。

Dim cell as Excel.Range
Dim rngCellsToProcess as Excel.Range

Set rngCellsToProcess = 'whatever you set it to
For each cell in rngCellsToProcess 
   'do something
Next cell

これは Set の必要性を排除するものではありませんが、何が起こっているのかをより明確にしながら、それを使用することを思い出させるのに役立つかもしれません.

于 2012-09-09T23:11:57.603 に答える
0

独自のカスタム関数を作成して、代わりに使用することはできますか?

Sub offset_rng(ByRef my_rng As Range, _
    Optional row As Integer, Optional col As Integer)
    Set my_rng = my_rng.Offset(row, col)
End Sub

次のように使用できます。

Sub test()
    Dim rng As Range
    Set rng = Range("A1")
    offset_rng my_rng:=rng, col:=1
    rng.Value = "test1"
    offset_rng my_rng:=rng, col:=1
    rng.Value = "test2"
    offset_rng my_rng:=rng, col:=1
    rng.Value = "test3"
    offset_rng my_rng:=rng, col:=1
    rng.Value = "test4"
End Sub
于 2012-09-09T21:12:01.750 に答える