How to write C# constructor with inner exception param

499 views Asked by At

I'm trying to create a custom exception class in C# 12.0 (.NET 8.0, SDK Version: 8.0.100-rc.2.23502.2) that supports a constructor with an inner exception parameter. Here's the code I initially tried:

public class TokenAcquisitionException(string message) : Exception(message)
{
    private const string FailedToGetToken = "Failed to get token";
    public TokenAcquisitionException() : this(FailedToGetToken)
    {        
    }

    public TokenAcquisitionException(string message, Exception innerException) : base(message, innerException)
    {
        /* What do I put here? */
    }
}

However, when I try to compile this code, I get the following error:

Error CS8862 A constructor declared in a type with parameter list must have 'this' constructor initializer.

This error occurs on the
line public TokenAcquisitionException(string message, Exception innerException) : base(message, innerException).

I found a GitHub issue that discusses a similar error, but it's related to records in C# 9 and later, not classes. The issue suggests that every constructor in a record with a primary constructor must call the primary constructor using this, otherwise the compiler wouldn't know how to handle the primary constructor parameters.

I tried various approaches to resolve this issue, including creating a custom exception like TokenAcquisitionException and using this instead of base in the constructor.

public class TokenAcquisitionException(string message) : Exception(message)
{
    private const string FailedToGetToken = "Failed to get token";
    public TokenAcquisitionException() : this(FailedToGetToken)
    {
    }

    public TokenAcquisitionException(string message, Exception innerException) : this(message)
    {
        /* What do I put here? */
    }
}

However, none of these approaches worked.

Does anyone know how to correctly implement a constructor with an inner exception parameter in a C# class? Any help would be greatly appreciated.

I was expecting that I could call the base class constructor with the same constructor signature:

base(message, innerException)

Typically, we can invoke the analogous constructor to pass responsibility for instantiation to the base class when the base class already significantly sets up the object.

2

There are 2 answers

0
Sweeper On BEST ANSWER

The issue suggests that every constructor in a record with a primary constructor must call the primary constructor using this, otherwise the compiler wouldn't know how to handle the primary constructor parameters.

Yes, this applies to all primary constructors, now that primary constructors are available for classes too.

Just make the inner exception constructor your primary constructor instead.

public class TokenAcquisitionException(string message, Exception? innerException) : Exception(message, innerException)
{
    private const string FailedToGetToken = "Failed to get token";
    public TokenAcquisitionException() : this(FailedToGetToken)
    {        
    }

    // write the single-parameter constructor as a regular constructor
    public TokenAcquisitionException(string message) : this(message, null)
    {
    }
}
0
Mavaddat Javid On

I solved this problem by using a main constructor that is not the primary constructor. The primary constructor requires that every secondary constructor calls it using this, which was not what I wanted. Instead, I used a main constructor that inherits from the base class constructor, and then I could create secondary constructors that also call the base class constructor with different parameters. Here is the code I used:

/// <summary>
/// Exception to indicate failed token acquisition.
/// </summary>
/// <seealso cref="System.Exception" />
public class TokenAcquisitionException : Exception
{
    public TokenAcquisitionException() : base($"{BaseMessage}.")
    {

    }

    public TokenAcquisitionException(string specificMessage) : base($"{BaseMessage}: {specificMessage}")
    {
    }

    public TokenAcquisitionException(string specificMessage, Exception innerException) : base($"{BaseMessage}: {specificMessage}", innerException)
    {
    }
}

This way, I can create a custom exception that supports an inner exception parameter without getting the compiler error. I hope this helps anyone who encounters a similar issue.