リリース・改善中
C# ガイド · 5/6
この章は現在、英語でのみ提供しています。
C# reports errors with exceptions. When one is thrown, execution unwinds up the call stack until a matching catch block handles it. This chapter covers handling exceptions, cleaning up with using, custom exception types, and unit testing with dotnet test.
The first catch whose type matches wins, so list specific types before general ones. finally always runs, which makes it the place for cleanup.
try
{
var text = File.ReadAllText("config.json");
var port = int.Parse(text.Trim());
Console.WriteLine($"Port: {port}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"Config file not found: {ex.FileName}");
}
catch (FormatException)
{
Console.WriteLine("The port number is not a valid integer.");
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
Console.WriteLine($"Could not read the file: {ex.Message}");
}
finally
{
Console.WriteLine("Finished reading configuration.");
}The when clause is an exception filter: the handler runs only if the condition holds. To rethrow, use a bare throw; so the original stack trace survives; throw ex; restarts the trace and hides the real origin.
When failure is routine, as with user input, prefer the Try pattern: int.TryParse(input, out var value) just returns false.
Fail fast on bad arguments using the built-in throw helpers. For domain errors, such as a broken business rule, derive your own type from Exception so callers can catch exactly that case.
public class InsufficientFundsException : Exception
{
public InsufficientFundsException(decimal balance, decimal requested)
: base($"Cannot withdraw {requested:F2} from a balance of {balance:F2}.")
{
Balance = balance;
Requested = requested;
}
public decimal Balance { get; }
public decimal Requested { get; }
}
public class Wallet(decimal balance)
{
public decimal Balance { get; private set; } = balance;
public void Withdraw(decimal amount, string reason)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
ArgumentException.ThrowIfNullOrWhiteSpace(reason);
if (amount > Balance)
throw new InsufficientFundsException(Balance, amount);
Balance -= amount;
}
}Exception class names end in Exception by convention. Wallet(decimal balance) is a primary constructor, whose parameter is visible across the class.
Files, sockets, and database connections implement ; releases what they hold, and guarantees that call even when an exception is thrown.
IDisposableDisposeusing// using statement: disposed at the end of the block (the file is closed)
using (var writer = new StreamWriter("log.txt", append: true))
{
writer.WriteLine($"{DateTime.Now:O} started");
}
// using declaration: disposed when the enclosing scope ends
using var reader = new StreamReader("log.txt");
Console.WriteLine(reader.ReadLine());
// your own types work with using once they implement IDisposable
using (new ElapsedTimer())
{
Thread.Sleep(100);
}
sealed class ElapsedTimer : IDisposable
{
private readonly System.Diagnostics.Stopwatch _watch = System.Diagnostics.Stopwatch.StartNew();
public void Dispose() => Console.WriteLine($"Elapsed: {_watch.ElapsedMilliseconds} ms");
}The compiler turns using into try/finally for you. For asynchronous cleanup (IAsyncDisposable), use await using.
The main .NET test frameworks are xUnit, NUnit, and MSTest. Assuming Wallet lives in a src/Wallet library, add an xUnit project like this:
dotnet new xunit -o tests/Wallet.Tests
dotnet sln add tests/Wallet.Tests
dotnet add tests/Wallet.Tests reference src/Wallet
dotnet test # run every test
dotnet test --filter "FullyQualifiedName~Withdraw" # run tests whose names matchusing Xunit;
public class WalletTests
{
[Fact]
public void Withdraw_DecreasesBalance()
{
var wallet = new Wallet(100m);
wallet.Withdraw(30m, "lunch");
Assert.Equal(70m, wallet.Balance);
}
[Fact]
public void Withdraw_MoreThanBalance_Throws()
{
var wallet = new Wallet(10m);
var ex = Assert.Throws<InsufficientFundsException>(() => wallet.Withdraw(50m, "book"));
Assert.Equal(50m, ex.Requested);
}
[Theory]
[InlineData(0)]
[InlineData(-100)]
public void Withdraw_InvalidAmount_Throws(int amount)
{
var wallet = new Wallet(10m);
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.Withdraw(amount, "bad amount"));
}
}A [Fact] takes no inputs; a [Theory] runs once per [InlineData] row. Arrange, Act, Assert keeps each test easy to scan.
MSTest, Microsoft's own framework, differs mainly in naming. Start with dotnet new mstest.
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class WalletTests
{
[TestMethod]
public void Withdraw_DecreasesBalance()
{
var wallet = new Wallet(100m);
wallet.Withdraw(30m, "lunch");
Assert.AreEqual(70m, wallet.Balance);
}
}| Task | xUnit | MSTest |
|---|---|---|
| Test method | [Fact] | [TestMethod] |
| Parameterized test | [Theory] + [InlineData] | [TestMethod] + [DataRow] |
| Equality check | Assert.Equal | Assert.AreEqual |
| Per-test setup | constructor | [TestInitialize] |
try/catch/finally, catch specific types first, and rethrow with throw;.Try pattern for routine failures and custom exceptions for domain errors.IDisposable objects in using so cleanup always runs.dotnet new xunit (or mstest) and run it with dotnet test.
コメント 0件
ログイン · ログインするとコメントできます。
最初のコメントを書いてみましょう。