ここから本文です

VBAのCopyの使い方についてエラーが下記のように でるものと,成功するものがあり...

ris********さん

2019/6/1723:50:31

VBAのCopyの使い方についてエラーが下記のように
でるものと,成功するものがあります
VBA得意な方にご教授いただきたいです。

【エラーがでる1004:RangeクラスのCopyメソッドが失敗】
Dim sh as worksheet
Set sh = Worksheets("Sheet1")

With sh
.Range("A1:C1").Copy(.Range("A2"))
End with

【成功する】
With Worksheets("Sheet1")
.Range("A1:C1").Copy(.Range("A2"))
End with


【成功する】
Dim sh as worksheet
Set sh = Worksheets("Sheet1")

With sh
.Range("A1:C1").Copy .Range("A2")
End with

【成功する】
Dim sh as worksheet
Set sh = Worksheets("Sheet1")

With sh
call .Range("A1:C1")Copy(.Range("A2"))
End with

違いが判りません><

閲覧数:
220
回答数:
4
お礼:
50枚

違反報告

ベストアンサーに選ばれた回答

kik********さん

2019/6/1809:45:45

良くわかってませんが、以下確認用で


Public Sub test()
   Call test1
   test2
   Call test3
End Sub

Private Sub test1()
   Dim i As Long, j As Long

   i = 1: j = i
   Call testsub(i)
   MsgBox "test1" & vbCrLf & "i:" & i & vbCrLf & "j:" & j
End Sub

Private Sub test2()
   Dim i As Long, j As Long

   i = 1: j = i
   testsub i
   MsgBox "test2" & vbCrLf & "i:" & i & vbCrLf & "j:" & j
End Sub

Private Sub test3()
   Dim i As Long, j As Long

   i = 1: j = i
   testsub (i)
   MsgBox "test3" & vbCrLf & "i:" & i & vbCrLf & "j:" & j
End Sub

Private Sub testsub(i As Long)
   i = 10
End Sub



test を実行すると test3 のみ、i = 1 状態になります
これは、
   testsub (i)
で呼び出した際、i を評価したものを渡すので、
実際の i を渡していないため、戻り値は受け取らない

.Range("A1:C1").Copy(.Range("A2"))

これは、.Range("A2") が評価され、
Range ではなくなったことにより Copy が失敗?


With sh と
With Worksheets("Sheet1") で動きが異なるのはわかりませんが
意図した記述にしている??からみると、( ) は付けない・・・になるかも


滅多にありませんが、
値を更新する関数を呼ぶ時、値を更新させたくない・・・で、使ったりします

  • kik********さん

    2019/6/1810:14:07

    変更

    以下にしないと、
    引数で渡した値が確認できませんでしたね


    Private Sub testsub(i As Long)
       i = 10
    End Sub



    Private Sub testsub(i As Long)
       Debug.Print "i:"; i
       i = 10
    End Sub

返信を取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

この回答は投票によってベストアンサーに選ばれました!

ベストアンサー以外の回答

1〜3件/3件中

並び替え:回答日時の
新しい順
|古い順

プロフィール画像

カテゴリマスター

hot********さん

2019/6/1902:59:43

興味深い質問なので見ていたのですが・・・





まず、質問とは直接関係ありませんが、tri********さんの解答の中にある

>その構文のなかで引数を()で囲うと「引数を強制的に値渡しにする」という意味を持ちます。

と言う部分は、結果的に値渡しと同じ結果になるけれど、値渡しにしてるわけではないと思います。
括弧が付くと式として評価され、評価結果が引数としてプロシージャに参照渡しされるので、書き換えられるのは他の変数で、元の変数は変わらないという事だと思います。
下を試してみて下さい。
test dmy
で呼び出した場合は、関数内の引数は元の変数と同じアドレスですが、
test (dmy)
で呼び出した場合は、別のアドレスになっています。
関数内でこの変数を書き換えても元の変数(dmy)は変わらないので値渡しのように見えるという事だと思います。

Sub sample()
Dim dmy As Integer
MsgBox VarPtr(dmy)
test dmy
test (dmy)
End Sub

Sub test(n As Integer)
MsgBox VarPtr(n)
End Sub





最初の質問について

よくわからないのですが、gek********さんが言っている

>この括弧は (2+3)*4 とかの場合と同じ、演算の優先順位を示す括弧として扱われています。

が正しいと思います。
上で言っている、「括弧が付くと式として評価され」という問題が起こっているのだと思います。
まずプロシージャの呼び出しで

プロシージャ名 引数の式
Call プロシージャ名(引数の式)
dmy=プロシージャ名(引数の式)

の下の2つの括弧はプロシージャの使い方の構文(Syntax)の括弧で、付けなくてはいけないルールです。
ここに、更に式を括弧でくくった場合の括弧は、式の優先順位の括弧だと思います。
質問の.Copyも
range.Copy 式
が基本の構文で、
range.Copy (式)
になると、式が評価されるのだと思います。
更に問題は、式の評価の途中で、式の中のRange()はRangeオブジェクトのつもりで使っているのに、プロパティがないので、勝手に.Valueを付けて評価しているようなのです。
下を試してください。
sh.Range("A2")
だけがRangeオブジェクトでなくなっています。
同様の事が起こるので、
Range("A1:C1").Copy(sh.Range("A2"))
の「(sh.Range("A2"))」の部分が「sh.Range("A2").value」と評価され、例えば
Range("A1:C1").Copy "ABC"
とかだと思われエラーになるのだと思います。

Sub sample()
Dim sh As Worksheet
Set sh = Worksheets("Sheet1")
MsgBox TypeName(Sheets("Sheet1").Range("A2")) '成功する
MsgBox TypeName((Sheets("Sheet1").Range("A2"))) '成功する
MsgBox TypeName(sh.Range("A2")) '成功する
MsgBox TypeName((sh.Range("A2"))) '失敗する
End Sub





と言う訳で、
「必要な括弧以外の括弧を引数の式に付けると、式として評価されて、その過程でRangeオブジェクトに既定のプロパティが付く場合があって不都合が生じる場合がある。」
という事だと思います。
ただし「(sh.Range("A2")」は勝手に.Valueで評価されるのに、「(Sheets("Sheet1").Range("A2"))」はRangeのままなのかが不明です。
と言うのが質問への回答です。
内容的にはちょっと違うのですが、gek********さんの「2つはエラーが違う」と言う所あたりに答えがあるような気がします。
でもこの部分の答えはわかりません。

tri********さん

2019/6/1813:32:01

まず基本構文として

(1)値を返さないメソッド(または関数等)
オブジェクト.メソッド 引数

(2)値を返す関数(またはメソッド等)
rc = 関数(引数)
(※rc は値を受け取る変数)

(3)プロシージャ(またはDLL)の呼び出し
Call プロシージャ名(引数)

になります。
※(2)(3)の引数のところの()を外すと構文エラーになります。

その構文のなかで引数を()で囲うと「引数を強制的に値渡しにする」という意味を持ちます。
(1') オブジェクト.メソッド (引数)
(2') rc = 関数((引数))
(3') Call プロシージャ名((引数))

このとき引数を値渡しできないものはエラーになります。
今回の事例の sh のように AS Worksheet や As Workbook などのオブジェクト変数として宣言した変数は基本的には値渡しすることはできません。(受け取り側でByValで値渡しとして受け取ることは可能)
sh を As Object とした場合や、シートではなくシート名として
Dim shName As String
With Worksheets(shName)
...(.Range("A1"))
とした場合は値渡し可能なのでエラーにはなりません。


結論ですが、上のような小難しいことを考えるよりは(特別な理由がなければ)、
「(1)(2)(3)の基本的な構文で記述すべき」
です。


※参考
他の回答として「不要な括弧が省かれるのでは?」という意見がありますが前述のとおり括弧には意味があるので省かれるわけではありません。
(3)と(3')で引数を渡して実験してみるときちんと違い(参照渡し or 値渡し)が確認できると思います。

返信を取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

プロフィール画像

カテゴリマスター

gek********さん

2019/6/1812:52:56

質問では
.Range("A1:C1").Copy(.Range("A2"))
と書かれていますが、Callステートメントなしの場合、実際には
.Range("A1:C1").Copy (.Range("A2"))
と Copy の後ろにスペースが入るはずです。

これは Range.Copy 引数 という形だからです。
本来、ここに括弧は必要ありません。この括弧は (2+3)*4 とかの場合と同じ、演算の優先順位を示す括弧として扱われています。
Call Range.Copy(引数)
の場合は、Callステートメントの仕様上、そこに括弧は必要であり、この括弧は演算の優先順位を示すものではありません。
Call .Range("A1:C1").Copy((.Range("A2")))
ともう一個括弧を増やせば、内側のは、演算の優先順位を示す括弧になりますから、同じようにエラー1004 が発生するはずです。

あとはなぜ (Worksheets("Sheet1").Range("A2")) だとエラーにならないかですね。
実験として、Rangeを、Rang とわざとスペルミスしてみます。
(Sh.Rang("A2"))
として実行しようとすると、コンパイルエラーになりますが、
(Worksheets("Sheet1").Rang("A2"))
だとコンパイルエラーではなく実行時エラーになります。
同じように、Rangeプロパティを使っていても、前者のようにWorksheet.Rangeではコンパイル時にチェックされるのに対し、後者のようにコレクションを使ったWorksheets._Default(Item).Rangeでは実行時まで保留されているようです。
つまり
(Sh.Range("A2"))
はコンパイル時に可能な範囲で式として評価しようとしますので、Sh.Range("A2")._Default を調べる中間コードにコンパイルされるのに対し、
(Worksheets("Sheet1").Range("A2"))
コンパイル時には単に不要な外側の括弧が省かれるだけなのではないでしょうか。
結果的に既定のプロパティではなくて、Rangeオブジェクトそのものが渡されるのでエラーにならないのだと思います。

返信を取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

この質問につけられたタグ

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる