紹介
業務でVisual Studio2022を利用して、Webアプリ(VB.Net)を開発しています(歴3年)。また、C#での開発も勉強中です。あの時知っていれば、と思ったエラーと対処法の共有です。
これを知ることで、自分のコードでエラーが起きた際、正しく対処できるはずです。
前提 & 環境
Visual Studio 2022を利用して、C#とVB.Netのサンプルコードを書きました。
初学者向けに、コードを簡単に書いている部分も一部あります。
VB.Netでは、実行でエラーとなるが、C#では、コンパイラでエラーとなるコードもあるため、完全に一致していないサンプルコードもあります。
Windows 11 home
Visual Studio Community 2022 17.14.3
C# .netFramework 4.7.2
VB.Net Framework 4.7.2
内容
エラーを英語で書いていますが、日本語でも説明をするので、
安心してください。
- NullReferenceException
- IndexOutOfRangeException
- InvalidCastException
- FormatException
- まとめ
1.NullReferenceException
1.NullReferenceException
一言でいうと「中身がないものを使おうとして失敗したエラー」です。
サンプルコードで、確認をしてみましょう。
C#
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
string sampleString = null;
int sampleLength = sampleString.Length;
}
}
}
VB.Net
Option Strict Off
Module Module1
Sub Main()
Dim sampleString As String
Dim sampleLength As Integer = sampleString.Length
End Sub
End Module
このコードを実行した際のメッセージを確認してみましょう。
C#
同様のメッセージですよね(nullとNothingで表現は違いますが)。
どちらも「中身がないものを使おうとして失敗したエラー」という形になります。
プログラマーには、Nullが好きな方はいないと思います。
こんな簡単なミスはしないぞと思う方もいると思いますが、
コードを長く書いたり、複雑なものだったり、共同で作業を行ったりすると起こしてしまいます。
C#の方は、意図的に変数にNullを仕込みました。
また、VB.Netの方は構文ミスです。
対処法のプログラムを書いてみます。
C# 対処法1
Nullが入る可能性のある場合は、Nullでないかを判定する。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
string sampleString = null;
//nullでないか判定
if (sampleString != null)
{
int sampleLength = sampleString.Length;
}
}
}
}
C# 対処法2
Nullを扱わない。
Null→""とする。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
string sampleString = "";
int sampleLength = sampleString.Length;
//int sampleInt;
//sampleInt = sampleInt + 1;
}
}
}
上記の方法が挙げられます。
*付け加えると、C#8.0では、プロジェクト単位、ファイル単位でのNullの禁止が行えるようになっています。新規かつNullを使わないのであれば、そちらの設定を行うとよいかと思います。
VBの方も挙げます。
VB.Net Nullでないかを判定する。
説明 Option Strict On
暗黙の型変換を許さない。必ず、型変換を行うこと!
Nullが入る可能性のある場合
Option Strict On
Module Module1
Sub Main()
Dim sampleString As String
'Nullでないか判定する
If Not IsNothing(sampleString) Then
Dim sampleLength As Integer = sampleString.Length
End If
End Sub
End Module
VB.Netの場合、初期値を入れる。
変数 As String = ""とする。
Option Strict On
Module Module1
Sub Main()
Dim sampleString As String = ""
'Nullでないか判定する
If Not IsNothing(sampleString) Then
Dim sampleLength As Integer = sampleString.Length
End If
End Sub
End Module
VB.Netの頭につけているOption Strict Onについて
VB.Netは型変換が緩いため、つけておくべき規則です。勝手にStringをIntegerに入れようとすると、コンパイラがエラーを起こしてくれます。
「本番コードでは Option Strict On を推奨」します。
実行前に気づける点が利点です!
コメント
変数にNullが、入るかもしれないことを想定しましょう!
2.IndexOutOfRangeException
次のエラーです。
一言でいうと、「存在しない番号にアクセスしたエラー」です。
どちらも配列を利用していますが、実行時にエラーとなります。
C#とVB.Netで異なるプログラムを書いています。
C#の場合
配列の範囲外の値を指定
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
int[] oneDimArray = new int[3]{ 1, 2 ,3};
int thirdValue = 0;
thirdValue = oneDimArray[3];
}
}
}
VBの場合、
For文を使用
Lengthの値まで(0,1,2,3まで)増えていっています。
Option Strict Off
Module Module1
Sub Main()
'配列
Dim oneDimArray() As Integer = {1, 2, 3}
'合計
Dim sum As Integer = 0
For i = 0 To oneDimArray.Length
sum = sum + oneDimArray(i)
Next
End Sub
End Module
C#もVB.Netも配列の要素は、0から始まっていきます。
なので、1から数えた値を入れたり、Length - 1を入れないと、
エラーが起きてしまいます。
実際に動かしてみるデバッグの時でないと、気づかなかったりするので注意が必要です。
実行時のエラー
それぞれのプログラムを直してみました。
最後の値を取り出したい場合は、要素数 - 1と書くことで、取り出せます。
C#の場合
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
int[] oneDimArray = new int[3] { 1, 2, 3 };
int thirdValue = 0;
thirdValue = oneDimArray[oneDimArray.Length - 1];
}
}
}
*下のVB.Netの例のようにFor Eachを利用することが、C#でも可能です。
VB.Netの場合
誤りの可能性があるFor文を使わずにFor Each文を利用して
配列の要素を1つずつ取り出すことで、対応しています。
Option Strict Off
Module Module1
Sub Main()
'配列
Dim oneDimArray() As Integer = {1, 2, 3}
'合計
Dim sum As Integer = 0
For Each value As Integer In oneDimArray
sum = sum + value
Next
End Sub
End Module
コメント2
配列といったまとまりを扱う場合は、範囲外の値を使わないように、気をつけましょう。
3.InvalidCastException
続いてのエラーは、
無効な型変換
「変えられない型に無理やり変えようとしたエラー」
例)文字列を数値に変えようとしたけど、できなかった。
といったケースです。
C#の場合
Object型の値を無理やり数値型に変更しようとしているため、エラーが起きます。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
object sampleObject = "123";
int intSample = (int)sampleObject + 1;
}
}
}
VB.Netの場合です。
C#と同様に、型変換でエラーが起きています。
Option Strict On
Module Module1
Sub Main()
Dim value As Object = "123"
Dim number As Integer = DirectCast(value, Integer) + 1
End Sub
End Module
といった具合で、Object型から、いきなり変換しようとしてはいけません。
こういった場合、値が入っていない(Nullの場合)も考慮して、下記のように書きます。
C#の場合
!= を利用して、nullでないことを確認。
&& を利用。左から1つ目の条件式が成り立たない場合、2つ目以降の式は、実行されない。
int.TryParseを使用
(1つ目の式の値をint型に変換、その結果を引数に入れる)。
int.TryParseは、型変換に失敗した場合は、False。成功した場合は、Trueを返す。
ToStringを使用(Object型の値を文字列として変換する、nullに対して行うとエラーが起きる)。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
object sampleObject = "123";
int intSample = 0;
if (sampleObject != null && int.TryParse(sampleObject.ToString(), out intSample))
{
intSample = intSample + 1;
}
}
}
}
VB.Netの場合
IsNothingで、null(Nothing)出ないことを確認。
AndAlsoは、を利用。左から1つ目の条件式が成り立たない場合、2つ目以降の式は、実行されない。
Integer.TryParseを利用する。(1つ目の式の値をint型に変換、その結果を引数に入れる)。
型変換に失敗した場合は、False。成功した場合は、Trueを返す。
ToStringを使用(Object型の値を文字列として使用する。nullに対して行うと、エラーが起きる)。
Option Strict On
Module Module1
Sub Main()
Dim value As Object = "123"
Dim number As Integer = 0
If Not IsNothing(value) AndAlso Integer.TryParse(value.ToString(), number) Then
number = number + 1
End If
End Sub
End Module
コメント3
指定した変数の型で変換できない場合を想定しましょう。
4.FormatException
一言でいうと、指定外の書式エラー
先に挙げたInvalidCastExceptionと紛らわしいですが、
こちらは、値自体が変換できないものだった場合に起きます。
例)そもそも変換できない文字列を整数にしようとした」
C#の例です。
ABCという文字列を整数に変換しようとしています。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
string hoge = "ABC";
int hogeInt = int.Parse(hoge);
}
}
}
VB.Netの場合、
C#と同様に、ABCDEという文字列を整数に変換しようとしています。
Option Strict Off
Module Module1
Sub Main()
Dim hoge As String = "ABCDE"
Dim hogeInt As Integer = Integer.Parse(hoge)
End Sub
End Module
それぞれの実行結果を見てみます。
これらのプログラムのエラー対策は、先に挙げたInvalidCastExceptionのプログラムと同様です。
修正例
C#の場合
InvalidCastExceptionとも一部重なりますが、
hoge != null はnull出ないことを確認
&& を利用。左から1つ目の条件式が成り立たない場合、2つ目以降の式は、実行されない。
TryParseで変換(1つ目の式で、string型の値をint型に変換。2つ目の式に結果の値を入れる)。
型変換に失敗した場合は、False。成功した場合は、Trueを返す。
文字列型 stringなので、ToString()は使う必要がないといった形です。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ErrorProgram
{
internal class Program
{
static void Main(string[] args)
{
string hoge = "ABC";
int hogeInt = 0;
if (hoge != null && int.TryParse(hoge, out hogeInt))
{
//hogeIntへの処理
}
}
}
}
VB.Netの場合
Not IsNothing(hoge)はNullでないことを確認
AndAlso を利用。左から1つ目の条件式が成り立たない場合、2つ目以降の式は、実行されない。
TryParseで変換(1つ目の式で、String型の値をint型に変換。2つ目の式に結果の値を入れる)。
型変換に失敗した場合は、False。成功した場合は、Trueを返す。
文字列型 Stringなので、ToString()は使う必要がないといった形です。
Option Strict On
Module Module1
Sub Main()
Dim hoge As String = "ABCDE"
Dim hogeInt As Integer = 0
If Not IsNothing(hoge) AndAlso Integer.TryParse(hoge, hogeInt) Then
'hogeIntの処理
End If
End Sub
End Module
コメント4
ユーザー入力は、変換に失敗する可能性あることも、想定しておきましょう!
といった具合です。
お疲れ様です。
5.まとめ
まとめ表
例外(Exception) | 原因 | 防ぎ方 |
---|---|---|
NullReference | Nullに触れた | Nullかをチェック or 非Null値を入れる |
IndexOutOfRange | 範囲外に出た | Length-1 or foreach |
InvalidCast | 無効な変換 | TryParse,TryCast |
Format | 形式が問題あり | TryParse |
コードと見比べてみた結果は、どうでしょうか?
エラーは、プログラムの実行の失敗を伝えてくれる。
バグは、プログラマーが埋め込んでしまう失敗です。
業務を行う上では、バグを避けるため、慎重に扱いましょう。
例え、エラーだったとしても見逃してしまったら、
それはバグとなってしまいます。
VB.NetやC#を利用している方々の少しでも助けになればと思います。