Skip to main content
ProductsDesktop toolsTASTECADDrawing reviewTASTEFILESFile managementTASTEFTPFile transferTASTESSHRemote workspaceTASTEZIPCompression and extractionDeveloper toolsTASTECODERust integrated development environmentTASTESTUDIOIntegrated development and test environmentTASTEETLData integration (ETL)ServersTASTEREMOTERemote desktop serverTASTEGEOGeospatial serverTASTEMAILMail workspaceGet startedDownloadsDocumentationBuyAll products
Marketplace
News
TechNewsToday's dev newsDeveloper docsProgramming languagesRustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDartWeb front-end frameworksNext.jsReactVue.js · NuxtSvelteKitAngularServer frameworksSpring BootDjangoFastAPIASP.NET CoreNestJS · ExpressApp & desktop frameworksTauriFlutterReact NativeElectronAlgorithmsAlgorithm basicsCore algorithmsAdvanced algorithmsPractical algorithms
CommunityProjectsDevelopment logsPromptsAI tool issuesShowcaseCommunity guide
Work with usTech transferWebsite buildDesktop app buildRelease and payment pipelineSales partnerContact
ServicesRemote desktopCustom installer packages
TASTEDEV
Products

Desktop tools

TASTECADComplex drawings, opened with easeTASTEFILESJust the files you need, as fast as possibleTASTEFTPThe tidiest path for your filesTASTESSHA stronger link between you and your serversTASTEZIPPack faster, unpack safer

Developer tools

TASTECODEEdit, build, debug and test Rust apps in one windowTASTESTUDIOFrom editing to remote tests and approved AI fixesTASTEETLMove data while you watch the values

Servers

TASTEREMOTEA remote desktop you run on your own serverTASTEGEORebuilding a GeoServer-compatible geospatial server in RustTASTEMAILTake back control of your email

Get started

DownloadsLatest builds of every productDocsHow to use each productBuySubscriptions and perpetual licencesAll productsEvery product at a glance
All products
MarketplaceNews
Tech

News

Today's dev newsOfficial announcements on languages, frameworks, GitHub and AIDeveloper docsGuides to languages, frameworks and algorithms

Programming languages

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

Frameworks

Web front-end frameworksNext.jsReactVue.js · NuxtSvelteKitAngular
Server frameworksSpring BootDjangoFastAPIASP.NET CoreNestJS · Express
App & desktop frameworksTauriFlutterReact NativeElectron

Algorithms

Algorithm basicsComplexity analysis · Sorting · Searching · Basic data structures · Trees and heapsCore algorithmsGraph traversal · Shortest paths & minimum spanning trees · Dynamic programming · Greedy algorithms · Divide and conquer · BacktrackingAdvanced algorithmsString algorithms · Math and number theory · Bit manipulation · Segment trees · Union-findPractical algorithmsCoding interview strategy · LRU cache · Rate limiting · Consistent hashing
All development docs
Community
ProjectsDiscover builders and their workDevelopment logsDecisions and lessons learnedPromptsReusable instructionsAI tool issuesProblems and workarounds for Claude, Codex, GeminiShowcaseFinished resultsCommunity guideHow we build in the open
Work with us
Tech transferNative apps in Rust and eguiWebsite build10 languages, accounts, docs and downloadsDesktop app buildRust and egui, no WebViewRelease and payment pipelineSigning, MSI, deb, rpm, AppImage, hashesSales partnerRecommend us and earn 30%ContactBuilds, partnerships and investment
Services
Remote desktopReach your computer from a browserCustom installer packagesOnly the features you need, settings preloaded
Buy
EN
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
Log in
Log in
TASTEDEV

From small tools to better development.

support@tastedev.netSupport TASTEDEV
ProductsProduct lineupBuyDocsDownloadsMarketplaceSupport
CommunityProjectsDevelopment logsPromptsAI tool issuesShowcase
PoliciesPrivacy policyTerms of serviceSubscription termsRefund policyCommunity guidelines
AccountLog inSign up
Visitors
Today
012345678901234567890123456789012345678901234567890123456789
Yesterday
012345678901234567890123456789012345678901234567890123456789
Total views
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
Company
GXSOFT
Representative
CHOI YOUNG GU
Business registration number
853-44-00368
Address
9F V24, 351 Gyeongin-ro, Michuhol-gu, Incheon, Republic of Korea
Phone
+82-10-4404-1523
Email
whitecalvin@tastedev.net
Privacy officer
CHOI YOUNG GU

© 2026 TASTEDEVProduct information is based on verified development sources.Released · improving

C# docs
C#
Getting Started with CSyntax BasicsTypes and DataProjects and PackagesErrors and TestingKey Concepts: LINQ, Delegates, and Async
Discussions

C# guide · 5/6

Errors and Testing

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.

try, catch, and finally

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.

Guard Clauses and Custom Exceptions

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.

using and IDisposable

Files, sockets, and database connections implement IDisposable; Dispose releases what they hold, and using guarantees that call even when an exception is thrown.

// 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.

Unit Testing with xUnit

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 match
using 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.

If You Prefer MSTest

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);
    }
}
TaskxUnitMSTest
Test method[Fact][TestMethod]
Parameterized test[Theory] + [InlineData][TestMethod] + [DataRow]
Equality checkAssert.EqualAssert.AreEqual
Per-test setupconstructor[TestInitialize]

Summary

  • Use try/catch/finally, catch specific types first, and rethrow with throw;.
  • Use the Try pattern for routine failures and custom exceptions for domain errors.
  • Wrap IDisposable objects in using so cleanup always runs.
  • Create a test project with dotnet new xunit (or mstest) and run it with dotnet test.
Questions about this chapter?
Previous chapterProjects and PackagesNext chapterKey Concepts: LINQ, Delegates, and Async

0 comments

Top commentsNewest first

Sign in · Sign in to leave a comment.