Sundstrom.CheckedExceptions
1.0.2
See the version list below for details.
dotnet add package Sundstrom.CheckedExceptions --version 1.0.2
NuGet\Install-Package Sundstrom.CheckedExceptions -Version 1.0.2
<PackageReference Include="Sundstrom.CheckedExceptions" Version="1.0.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Sundstrom.CheckedExceptions --version 1.0.2
#r "nuget: Sundstrom.CheckedExceptions, 1.0.2"
// Install Sundstrom.CheckedExceptions as a Cake Addin #addin nuget:?package=Sundstrom.CheckedExceptions&version=1.0.2 // Install Sundstrom.CheckedExceptions as a Cake Tool #tool nuget:?package=Sundstrom.CheckedExceptions&version=1.0.2
Checked Exceptions for C#
Enforce explicit exception handling in C#/.NET by ensuring all exceptions are either handled or declared.
This analyzer works with existing class libraries (including .NET class libraries) that have exceptions declared in XML documentation.
Table of Contents
- Features
- Installation
- Usage
- XML Documentation Support
- Configuration
- Suppressing Diagnostics
- Examples
- Contributing
- License
Features
- Enforce Explicit Exception Handling: Ensure all exceptions are either caught or explicitly declared using attributes.
- Seamless Integration: Works with existing .NET class libraries that include exceptions in their XML documentation.
- Custom Diagnostics: Provides clear diagnostics for unhandled or undeclared exceptions.
- Support for XML Documentation: Leverages XML docs to identify exceptions from unannotated libraries.
- **Code Fixes: Offers automated solutions to streamline exception handling and declaration.
Installation
You can install the package via the NuGet Gallery:
Install-Package Sundstrom.CheckedExceptions
Or via the .NET CLI:
dotnet add package Sundstrom.CheckedExceptionsAnalyzer
Usage
Defining ThrowsAttribute
To utilize CheckedExceptionsAnalyzer, you need to define the ThrowsAttribute
in your project. Here's a simple implementation:
using System;
namespace CheckedExceptions
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Delegate, AllowMultiple = true)]
public class ThrowsAttribute : Attribute
{
public Type ExceptionType { get; }
public ThrowsAttribute(Type exceptionType)
{
if (!typeof(Exception).IsAssignableFrom(exceptionType))
throw new ArgumentException("ExceptionType must be an Exception type.");
ExceptionType = exceptionType;
}
}
}
Notes:
- Namespace Choice: It's advisable to place
ThrowsAttribute
in a custom namespace (e.g.,CheckedExceptions
) rather than theSystem
namespace to avoid potential conflicts with existing .NET types.
Annotating Methods
Annotate methods, constructors, or delegates that throw exceptions to declare the exceptions they can propagate.
using CheckedExceptions;
public class Sample
{
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
// Some operation that might throw InvalidOperationException
throw new InvalidOperationException("An error occurred during the operation.");
}
}
Handling Exceptions
Ensure that any method invoking annotated methods either handles the declared exceptions or further declares them.
using System;
using CheckedExceptions;
public class Sample
{
public void Execute()
{
try
{
PerformOperation();
}
catch (InvalidOperationException ex)
{
// Handle invalid operations
}
}
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
// Some operation that might throw InvalidOperationException
throw new InvalidOperationException("An error occurred during the operation.");
}
}
Diagnostics
THROW001
(Unhandled Exception): In the Execute
method, if PerformOperation
throws an InvalidOperationException
that is neither caught nor declared using ThrowsAttribute
, the analyzer will report this diagnostic.
Example Diagnostic Message:
THROW001: Exception `InvalidOperationException` is thrown by `PerformOperation` but neither caught nor declared via `ThrowsAttribute`.
After properly handling the exception, no diagnostics are expected.
Code Fixes
CheckedExceptionsAnalyzer provides automated code fixes to simplify exception handling and declaration. These code fixes enhance developer productivity by offering quick solutions to common exception management scenarios.
1. Add ThrowsAttribute
Automatically adds the ThrowsAttribute
to methods that propagate exceptions without declarations.
Example:
Before Applying Code Fix:
public class Sample
{
public void PerformOperation()
{
// An exception is thrown but not declared
throw new InvalidOperationException("An error occurred.");
}
}
After Applying Code Fix:
using CheckedExceptions;
public class Sample
{
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
// An exception is thrown and declared
throw new InvalidOperationException("An error occurred.");
}
}
How to Apply:
- Visual Studio: Hover over the diagnostic warning, click on the light bulb icon, and select "Add ThrowsAttribute" from the suggested fixes.
- Visual Studio Code: Similar steps apply using the quick fix light bulb.
2. Add try-catch
Block
Provides an automated way to wrap code that may throw exceptions within a try-catch
block, ensuring that exceptions are properly handled.
Example:
Before Applying Code Fix:
public class Sample
{
public void Execute()
{
// An exception is thrown but not handled
PerformOperation();
}
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
throw new InvalidOperationException("An error occurred.");
}
}
After Applying Code Fix:
public class Sample
{
public void Execute()
{
try
{
PerformOperation();
}
catch (InvalidOperationException ex)
{
// Handle the exception
// TODO: Add exception handling logic here
}
}
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
throw new InvalidOperationException("An error occurred.");
}
}
How to Apply:
- Visual Studio: Hover over the diagnostic warning, click on the light bulb icon, and select "Add try-catch block" from the suggested fixes.
- Visual Studio Code: Similar steps apply using the quick fix light bulb.
Benefits:
- Consistency: Ensures that exception handling follows a standardized pattern across the codebase.
- Efficiency: Saves time by automating repetitive coding tasks related to exception management.
Notes:
- Customization: While the analyzer provides default templates for
try-catch
blocks, you can customize the generated code as needed to fit your project's specific requirements.
XML Documentation Support
CheckedExceptionsAnalyzer leverages XML documentation to identify exceptions from methods that do not have ThrowsAttribute
annotations. This is particularly useful for:
- Unannotated Libraries: Works with libraries that lack explicit exception annotations by using their XML documentation.
- .NET Class Libraries: Extends support to the .NET framework by reading exceptions documented in XML.
If a library has both XML docs with exceptions and ThrowsAttribute
annotations, the analyzer combines exceptions from both sources.
Example:
using System;
using System.IO;
public class FrameworkSample
{
public void WriteToConsole()
{
// THROW001: Exception `IOException` is thrown by `Console.WriteLine` but neither caught nor declared via `ThrowsAttribute`.
Console.WriteLine("Hello, World!");
}
}
// Note: The Console class below is a simplified mock for demonstration purposes.
/// <summary>
/// Writes the specified value, followed by the current line terminator, to the standard output stream.
/// </summary>
/// <param name="value">
/// The value to write to the output. Can be of various types (e.g., string, object, etc.).
/// </param>
/// <remarks>
/// This method writes a line of text to the console. It automatically appends a newline at the end of the output.
/// </remarks>
/// <exception cref="System.IO.IOException">
/// An I/O error occurred.
/// </exception>
public class Console
{
public void WriteLine(string value)
{
// Implemented in .NET
}
}
In the above example, the analyzer identifies that Console.WriteLine
can throw an IOException
based on the XML documentation, even though ThrowsAttribute
is not used.
Configuration
CheckedExceptionsAnalyzer offers flexible configuration options to tailor its behavior to your project's requirements. This includes adjusting diagnostic severities, treating specific warnings as errors, and customizing how exceptions are handled.
EditorConfig Settings
You can customize the analyzer's behavior using an .editorconfig
file. This allows you to enable or disable specific diagnostics and adjust their severity levels.
Example .editorconfig
Settings:
[*.cs]
# Enable or disable specific diagnostics
dotnet_diagnostic.THROW001.severity = warning
dotnet_diagnostic.THROW003.severity = warning
dotnet_diagnostic.THROW004.severity = warning
dotnet_diagnostic.THROW005.severity = warning
# Example of changing the severity of a diagnostic
# dotnet_diagnostic.THROW001.severity = error
Explanation:
dotnet_diagnostic.<DiagnosticID>.severity
: Sets the severity level for a specific diagnostic. Possible values arenone
,silent
,suggestion
,warning
, orerror
.
Treating Warnings as Errors
To enforce stricter exception handling by elevating specific warnings to errors, you can use the <WarningsAsErrors>
property in your project file. This is particularly useful for maintaining high code quality and ensuring that critical exception handling issues are addressed promptly.
Example: Turning Specific Warnings into Errors
To treat the nullable
warnings and the THROW001
diagnostic as errors, add the following property to your .csproj
file:
<PropertyGroup>
<WarningsAsErrors>nullable,THROW001</WarningsAsErrors>
</PropertyGroup>
Explanation:
WarningsAsErrors
: This MSBuild property allows you to specify a comma-separated list of warning codes that should be treated as errors during compilation.nullable
: This standard warning pertains to nullable reference type annotations and warnings introduced in C# 8.0 and later.THROW001
: This is the diagnostic ID for unhandled exceptions identified by CheckedExceptionsAnalyzer.
Result:
With the above configuration:
Any warnings related to nullable reference types (
nullable
) will be treated as errors, preventing the build from succeeding if such issues are present.The
THROW001
warnings, which indicate unhandled exceptions that are neither caught nor declared, will also be treated as errors, enforcing stricter exception handling practices.
Full .csproj
Example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<WarningsAsErrors>nullable,THROW001</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Sundstrom.CheckedExceptions" Version="1.0.0" />
</ItemGroup>
</Project>
Notes:
Selective Enforcement: By specifying only certain warnings in
WarningsAsErrors
, you can enforce stricter rules where necessary while allowing other warnings to remain as warnings.Gradual Adoption: This approach enables a gradual transition to more disciplined exception handling by focusing on the most critical diagnostics first.
Suppressing Diagnostics
While it's recommended to adhere to the analyzer's rules for optimal exception handling, there might be scenarios where you need to suppress specific diagnostics.
Using Pragma Directives
You can suppress diagnostics directly in your code using #pragma
directives. This method is useful for suppressing warnings in specific code blocks.
Example: Suppressing a Diagnostic Around a Method Call
#pragma warning disable THROW001 // Unhandled exception thrown
PerformOperation();
#pragma warning restore THROW001 // Unhandled exception thrown
Example: Suppressing a Diagnostic for a Specific Throw
#pragma warning disable THROW001 // Unhandled exception thrown
throw new InvalidOperationException();
#pragma warning restore THROW001 // Unhandled exception thrown
Suppressing with Attributes
Alternatively, you can suppress diagnostics using the [SuppressMessage]
attribute. This approach is beneficial for suppressing warnings at the method or class level.
Example: Suppressing a Diagnostic for a Method
using System.Diagnostics.CodeAnalysis;
[SuppressMessage("Usage", "THROW001:Unhandled exception thrown")]
public void MethodWithSuppressedWarning()
{
// Method implementation
throw new InvalidOperationException();
}
Caution: Suppressing diagnostics should be done sparingly and only when you have a justified reason, as it can undermine the benefits of disciplined exception handling.
Examples
Basic Usage
Annotating a Method:
using CheckedExceptions;
public class Calculator
{
[Throws(typeof(DivideByZeroException))]
public int Divide(int numerator, int denominator)
{
if (denominator == 0)
throw new DivideByZeroException("Denominator cannot be zero.");
return numerator / denominator;
}
}
Handling Exceptions Example
Handling Declared Exceptions:
using System;
using CheckedExceptions;
public class CalculatorClient
{
private Calculator _calculator = new Calculator();
public void PerformDivision()
{
try
{
int result = _calculator.Divide(10, 0);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Cannot divide by zero.");
}
}
}
In this example, the Divide
method declares that it can throw a DivideByZeroException
using ThrowsAttribute
. The PerformDivision
method handles this exception, thus complying with the analyzer's requirements.
Using Code Fixes
Example 1: Adding ThrowsAttribute
Before Applying Code Fix:
public class Sample
{
public void PerformOperation()
{
// An exception is thrown but not declared
throw new InvalidOperationException("An error occurred.");
}
}
Applying Code Fix:
Visual Studio: Hover over the diagnostic warning on the
throw
statement, click on the light bulb icon, and select "Add ThrowsAttribute".Resulting Code:
using CheckedExceptions;
public class Sample
{
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
// An exception is thrown and declared
throw new InvalidOperationException("An error occurred.");
}
}
Example 2: Adding try-catch
Block
Before Applying Code Fix:
public class Sample
{
public void Execute()
{
// An exception is thrown but not handled
PerformOperation();
}
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
throw new InvalidOperationException("An error occurred.");
}
}
Applying Code Fix:
Visual Studio: Hover over the diagnostic warning on the
PerformOperation
call in theExecute
method, click on the light bulb icon, and select "Add try-catch block".Resulting Code:
public class Sample
{
public void Execute()
{
try
{
PerformOperation();
}
catch (InvalidOperationException ex)
{
// Handle the exception
// TODO: Add exception handling logic here
}
}
[Throws(typeof(InvalidOperationException))]
public void PerformOperation()
{
throw new InvalidOperationException("An error occurred.");
}
}
Benefits of Code Fixes:
- Efficiency: Automates repetitive tasks, allowing developers to focus on core logic.
- Consistency: Ensures that exception handling follows a standardized pattern across the codebase.
- Error Reduction: Minimizes the risk of missing exception declarations or handlers.
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch (
git checkout -b feature/YourFeature
). - Commit your changes (
git commit -m 'Add some feature'
). - Push to the branch (
git push origin feature/YourFeature
). - Open a pull request.
Please ensure your code adheres to the project's coding standards and includes appropriate tests.
License
This project is licensed under the MIT License.
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on Sundstrom.CheckedExceptions:
Repository | Stars |
---|---|
marinasundstrom/YourBrand
Prototype enterprise system for e-commerce and consulting services
|
Version | Downloads | Last updated |
---|---|---|
1.2.3 | 128 | 11/30/2024 |
1.2.2 | 88 | 11/29/2024 |
1.2.1 | 92 | 11/28/2024 |
1.2.0 | 95 | 11/27/2024 |
1.1.9 | 97 | 11/26/2024 |
1.1.8 | 92 | 11/26/2024 |
1.1.7 | 94 | 11/26/2024 |
1.1.6 | 93 | 11/25/2024 |
1.1.5 | 91 | 11/25/2024 |
1.1.4 | 97 | 11/25/2024 |
1.1.3 | 107 | 11/24/2024 |
1.1.2 | 86 | 11/24/2024 |
1.1.1 | 88 | 11/24/2024 |
1.1.0 | 95 | 11/23/2024 |
1.0.9 | 101 | 11/22/2024 |
1.0.8 | 99 | 11/22/2024 |
1.0.7 | 96 | 11/21/2024 |
1.0.6 | 98 | 11/21/2024 |
1.0.5 | 94 | 11/20/2024 |
1.0.4 | 95 | 11/19/2024 |
1.0.3 | 94 | 11/19/2024 |
1.0.2 | 104 | 11/19/2024 |
1.0.1 | 103 | 11/19/2024 |
1.0.0 | 99 | 11/19/2024 |
Initial release