「オブジェクト参照が必要です」エラーが出たときは?[C#/VB]:.NET TIPS
C#やVBでプログラムを記述していると、たまに「オブジェクト参照が必要です」と怒られるときがある。その理由と、解決策を見ていこう。
C#/VBでコーディングしていると「オブジェクト参照が必要です」というエラーが出ることがある。本稿では、そのエラーはどのようなときに出るのか、また、その解決方法について解説する。
特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。
なお、本稿に掲載したサンプルコードをそのまま試すにはVisual Studio 2015以降が必要である。
こんなときにエラー:Windowsフォームの例(VB)
これは、VBを学び始めたときにつまずきやすい例である。VBでWindowsフォームのプロジェクトを作り、Form2を追加した。そして、Form1にボタンを配置して、それをクリックしたときにForm2を表示するコードを次のように書いたとしよう。
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
' Form2を表示する
Form2.Show()
End Sub
このコードは、ちゃんと動作する。
次に、Form2にAddメソッドを書いて、同じようにForm1から呼び出してみる(次のコード)。
' Form2.vb内
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
' Form1.vb内
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
' Form2のAddメソッドを呼び出す
Dim result1 = Form2.Add(1, 2)
End Sub
これも、期待通りに動作する。
ところで、ロジックは別のクラスに分けた方がよいというので、新しくクラスを作り、そこにAddメソッドを移した(次のコード)。
Public Class OriginalClass
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
End Class
Addメソッドの内容は、先ほどのForm2に書いてあったものと全く同じだ。
Addメソッドの場所をForm2からOriginalClassに移動させたので、先ほどのAddメソッドを呼び出している部分もそのように書き換える(次のコード)。
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
' Addメソッドを独自のクラスに移した
Dim result2 = OriginalClass.Add(1, 2) ' ←この行はコンパイルエラー
' BC30469 非共有メンバーを参照するには、オブジェクト参照が必要です。
End Sub
すると、どうしたことか! 「非共有メンバーを参照するには、オブジェクト参照が必要です」というエラーが出てしまうのだ(次の画像)。
なぜこのような違いが生じるのだろうか? それは暗黙のフォームインスタンスにある。詳しくは、「.NET開発者中心 厳選ブログ記事:.NET開発を始めるVB6プログラマーが知るべき9のこと」の「3.Form.Show()はしない」をご覧いただきたい。
このエラーの解決法は、Addメソッドを呼び出しているところに本稿の「解法1:オブジェクトを作る」を適用するか、または、Addメソッドに対して本稿の「解法2:静的メンバに変更する」を適用する。
こんなときにエラー:コンソールアプリの例
コンソールアプリに限らず、一般的にクラスのコードを書いているときに、たいていは「うっかりミス」をしてこのエラーを出してしまうことが多い。
まずC#の例から。コンソールアプリのプロジェクトを作り、Mainメソッドが置かれているProgramクラスにAddメソッドを追加したとしよう。それをMainメソッドから呼び出すとき、次のコードのようにエラーが出てしまうことがある。
class Program
{
// インスタンスメソッド
public int Add(int a, int b)
=> a + b;
// 静的メソッド
static void Main(string[] args)
{
var result1 = Add(1, 2); // ←この行はコンパイルエラー
// CS0120 静的でないフィールド、メソッド、またはプロパティ 'Program.Add(int, int)' で、オブジェクト参照が必要です
}
}
Mainメソッドのシグネチャ(メソッドの先頭でメソッド名などを宣言している部分)は自動生成されたものなので、この「うっかりミス」はやってしまいがちだ。
VBの場合は、呼び出しているメソッドが同じクラス内にあるか別クラスにあるかで、エラーメッセージが変わる。
VBでコンソールアプリのプロジェクトを作り、Mainメソッドが置かれているModule1モジュールにAddメソッドを追加したとしよう(次のコード)。それをMainメソッドから呼び出すと、うまく動く。そこで新しくSampleClassクラスを作り、そこにAddメソッドを移植し、それをMainメソッドから呼び出すと、今度はエラーになってしまう。また、そのSampleClassクラスに静的メソッドを作って、その中からAddメソッドを呼び出しても、エラーになってしまう。どちらも根本的なエラーの原因は同じなのだが、VBでは異なるエラーとして報告される。
Public Class SampleClass
'インスタンスメソッド
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
' 静的メソッド
Public Shared Sub SampleMethod()
Dim result1 = Add(1, 2) ' ←この行はコンパイルエラー
' BC30369 クラスの明示的なインスタンスを指定しないで、共有メソッドまたは共有メンバー初期化子内からクラスのインスタンス メンバーを参照することはできません。
End Sub
End Class
Module Module1
'静的メソッド
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
Sub Main()
Dim result1 = Module1.Add(1, 2) ' ←これはエラーにならない
Dim result2 = SampleClass.Add(1, 2) ' ←この行はコンパイルエラー
' BC30469 非共有メンバーを参照するには、オブジェクト参照が必要です。
End Sub
End Module
Mainメソッドの中では、モジュール内のAddメソッドを呼び出すとうまく動くのに、SampleClassクラスのAddメソッドを呼び出すとエラーになる。
SampleClassクラスのSampleMethod静的メソッドから、同じSampleClassクラスのAddメソッドを呼び出すとやはりエラーになるが、先ほどのエラーとはメッセージが違う。
C#より複雑な状況だ。
解法1:オブジェクトを作る
「オブジェクト参照が必要です」というエラーは、オブジェクト(=クラスのインスタンス)がないという意味だ。だから、ないのが問題ならば作ればよい、というのが解決策の1つになる。
「こんなときにエラー:Windowsフォームの例(VB)」でエラーの出た行は、次のコードのように修正できる。
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
'Dim result2 = OriginalClass.Add(1, 2) ' ←この行はコンパイルエラー
' BC30469 非共有メンバーを参照するには、オブジェクト参照が必要です。
' ↓オブジェクトを作り、そのオブジェクトに対して呼び出せばよい
Dim objectOfOriginalClass = New OriginalClass()
Dim result2a = objectOfOriginalClass.Add(1, 2)
' あるいは、次のようにオブジェクトの生成と呼び出しをいっぺんに書いてもよい
Dim result2b = (New OriginalClass()).Add(1, 2)
End Sub
コンソールアプリの例でも同様である。例えば、VBのSampleClassクラスの例は、次のコードのようにして修正が可能だ。
Public Class SampleClass
'インスタンスメソッド
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
' 静的メソッド
Public Shared Sub SampleMethod()
'Dim result1 = Add(1, 2) ' ←この行はコンパイルエラー
' BC30369 クラスの明示的なインスタンスを指定しないで、共有メソッドまたは共有メンバー初期化子内からクラスのインスタンス メンバーを参照することはできません。
' ↓オブジェクトを作り、そのオブジェクトに対して呼び出せばエラーにならない
Dim objectOfSampleClass = New SampleClass()
Dim result1a = objectOfSampleClass.Add(1, 2)
' 次のようにオブジェクトの生成と呼び出しをいっぺんに書くことも可能ではある
Dim result1b = (New SampleClass()).Add(1, 2)
End Sub
End Class
だが、ちょっと待ってほしい。SampleClassクラスの中にあるメソッドでSampleClassオブジェクトを作るというのは、ちょっと変ではないだろうか? そのような使い方もないわけではないが、クラスのオブジェクトはそのクラスとは別の場所で作るのが一般的だ。
「オブジェクト参照が必要です」とは、裏返せば「オブジェクトを必要としないようにできればよい」という意味でもある。場合によっては、そのような方法でも解決できる(本稿で挙げたエラーの例は、実は全てそうである)。次の「解法2」をご覧いただきたい。
解法2:静的メンバに変更する
「オブジェクト参照が必要です」というエラーになったところで呼び出しているメソッドは、本当にそのオブジェクトを必要としているだろうか? そうでないなら、オブジェクトを必要としない静的メンバに変更することで、エラーを解決できる。
「こんなときにエラー:コンソールアプリの例」は、Addメソッドを静的メンバに修正すれば解決する(次のコード)。
class Program
{
// インスタンスメソッド → 静的メソッドに変更
public static int Add(int a, int b)
=> a + b;
// 静的メソッド
static void Main(string[] args)
{
var result1 = Add(1, 2); // ←この行のエラーは消える
}
}
Public Class SampleClass
'インスタンスメソッド → 静的メソッドに変更
Public Shared Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
' 静的メソッド
Public Shared Sub SampleMethod()
Dim result1 = Add(1, 2) ' ←この行のエラーは消える
End Sub
End Class
Module Module1
……省略……
Sub Main()
……省略……
Dim result2 = SampleClass.Add(1, 2) ' ←この行のエラーは消える
End Sub
End Module
この例では、Addメソッドは、メソッドが格納されているクラスのオブジェクトを必要としていない(Addメソッド中で使っている変数は、引数として渡されたaとbだけである)。そのような場合は、メソッドを静的メンバに変えてしまえばよい。それには、メソッドのシグネチャにstatic修飾子(C#)/Shared修飾子(VB)を付ける。
このように静的メンバへ変更できるなら、そうした方がよい。オブジェクトを作るというのは、それなりに負荷のかかる処理である。オブジェクトを作らずに済めば、その分だけパフォーマンスが向上するからだ。VBの場合、オブジェクトを必要としないメソッドのみで構成される論理的なグループを作成したいのであれば(数学関数を集めたものなどが考えられる)、クラスではなくモジュールにそれらを記述することを検討してもよい。例えば、先ほどのSampleClassクラスはSampleModuleモジュールに書き換えられるはずだ。VBのモジュールに含まれるメンバーは暗黙的にSharedが付加されたものとして扱われるので、この場合はShared修飾子は必要ないことには注意しよう。
まとめ
「オブジェクト参照が必要です」というエラーは、インスタンスメンバを呼び出すときに静的メンバの呼び出し方をしてしまったミスマッチが原因だ。解決策は、そろえること。つまり、インスタンス(=オブジェクト)を作ってインスタンスメンバとしての呼び出し方に変えるか、あるいは、インスタンスメンバを静的メンバに修正するかである。静的メンバに修正できるなら、その方がオブジェクトを作る処理が減る分だけパフォーマンスがよくなる。
利用可能バージョン:C# 全て/Visual Basic 7(Visual Studio 2002)以降
カテゴリ:C# 処理対象:オブジェクト
カテゴリ:Visual Basic 処理対象:オブジェクト
関連TIPS:VB.NETのモジュールの正体は?
関連TIPS:構文:メソッドやプロパティをラムダ式で簡潔に実装するには?[C# 6.0/7.0]
Copyright© Digital Advantage Corp. All Rights Reserved.