C# – Throwing Exceptions from Constructor

cexceptions

I have this NamePath class that accepts a string from some system path and tries to split it into two properties, Name and LastName.

Is the code example below fine? I've read that throwing exceptions in constructor is okay, but is there a better way for this case?

public class NamePath{
   public string Name {get; private set; }
   public string LastName { get; private set; }

   public NamePath(string path){   
       Parse(path);
   }
   private void Parse(string path){
       if ( ParsingOkay() ) {
          // set the properties
       } else {
          throw new Exception
       }
   }

}

I've thought of another approach where NamePath is just data class, and I would have a static method on another class that would try to create NamePath object or else it would return null.

public class NamePath {
   public string Name {get; private set; }
   public string LastName { get; private set; }

   public NamePath (string name, string lastName){
       Name = name;
       LastName = lastName;
   }
}

public class PathHelper {
    public static NamePath ParseNamePath (string path){
        if ( ParsingOkay() ){
           return new NamePath
        } else {
           return null
        }
    }
}

code example is semi-pseudo

Best Answer

Throwing exceptions in constructors in C# is fine, but a constructor should always create a valid object. I prefer to keep construction devoid of parsing. Parsing is inherently tricky, because you cannot trust the source of the data. Exceptions should be expected when parsing anything. The .NET stack already has a pattern to deal with this: Parse and TryParse.

While a monad is more idiomatic across languages, the [Try]Parse pattern is a long standing idiom for C#.

The constructor always takes data in the necessary pieces, and throws ArgumentXExceptions in the case invalid arguments are passed. The parse methods can be static methods to handle the parsing either by creating a valid object or throwing a FormatException.

var path = new NamePath(name, lastName); // throws Argument*Exception

try
{
    var path = NamePath.Parse("...");

    // Use properly constructed path
}
catch (FormatException)
{
    // Parse errors
}

if (NamePath.TryParse("...", out NamePath path))
{
    // User properly constructed NamePath as path variable
}
else
{
    // Parse errors
}

This gives code authors some options with how to construct, parse and recover from errors. You'll see this pattern reflected in the native .NET types DateTime, int, decimal, etc. Your custom NamePath type will feel more natural to deal with along side the built-in types:

if (int.TryParse("...", out int age))
{
    // valid int
}
else
{
    // invalid int
}

if (NamePath.TryParse("...", out NamePath path))
{
    // valid NamePath
}
else
{
    // invalid NamePath
}