1
\$\begingroup\$

As part of a C# class CsvConverter that converts various file types to CSV, I am designing a private class CsvConversionResult. My current design choice is to return a null value of CsvConversionResult if processing is successful.

What can I add to or remove from my design to use "best practices"? Returning primitive data types is out of the question since I want to return more than one thing.

public static class CsvConverter
{
    public class CsvConverterError
    {  
        public string Message { get; set; }
        public int LineNumber { get; set; }
        public string FileName { get; set; }

        public CsvConverterError(string message, int lineNumber, string fileName)
        {
            Message = message;
            LineNumber = lineNumber;
            FileName = fileName;
        }
    }

    // returns null if successful
    public static CsvConverterError WriteCsvRowsToFile(char delimiter, 
                                                 string inputFilePath, 
                                                 string outputFilePath, 
                                                 string qualifier = "")
    {
        CsvConverterError error = null;

        /* Some code here that sets the value of error accordingly */

        return error;
    }
\$\endgroup\$
12
  • 3
    \$\begingroup\$ "Returning primitive data types is out of the question since I want to return more than one thing." Could you expand on this? What would a consumer who can handle both success and failure results look like? \$\endgroup\$ Jul 22, 2014 at 11:01
  • 1
    \$\begingroup\$ @BenAaronson When an error occurs in ConvertFile() I want to return the line number, an error message, and the paths of the source and destination files, so the return type has to be a custom class. My current plan is that if the user of the class calls ConvertFile() and gets a CsvConverterSuccess object returned, they know from the method's <returns> XML tag that they have nothing more to do. Otherwise, they get a CsvConverterError object which holds information about where and what type of error occurred. \$\endgroup\$
    – SNN
    Jul 22, 2014 at 11:09
  • \$\begingroup\$ Maybe I am overthinking this. Now I'm wondering if it's a better idea to scrap inheritance, just use one class CsvConverterResult and give it a boolean property Success. \$\endgroup\$
    – SNN
    Jul 22, 2014 at 11:19
  • \$\begingroup\$ Actually looking at this code, I'm not sure it's quite finished enough to review. You have stuff like CsvConverterError's empty constructor which presumably should be setting those properties, you have things private which I'd have thought would need to be public. \$\endgroup\$ Jul 22, 2014 at 12:32
  • 1
    \$\begingroup\$ Why not use an exception? \$\endgroup\$
    – svick
    Jul 22, 2014 at 14:03

1 Answer 1

1
\$\begingroup\$

As one of the commenters pointed out, adding a boolean flag to your result is one of the better ways to go about returning a successful result, especially when compared to returning null. In fact, I have a wrapper around a search result object where I do exactly this in the code for my current project.

"null" makes me think of "something wrong".

This is exactly why null should not be used for this situation. At worst you'll have a programmer later who is totally confused about what is going on in your code, as most people will not return null from a function, especially for successful completion of that code.

This is the code I use:

public class SearchMemberEligibilityWrapper
{
    public List<RegularMember> ResultList { get; set; }
    public int ResultCount { get; set; }
    public bool ResultSuccessful { get; set; }
    public SearchType ResultSource { get; set; }
    public string ResultError { get; set; }

    public SearchMemberEligibilityWrapper()
    {
        this.ResultList = new List<RegularMember>();
        this.ResultCount = 0;
        this.ResultSuccessful = false;
        this.ResultSource = SearchType.Default;
        this.ResultError = string.Empty;
    }

    public enum SearchType
    {
        Default,
        QNXT,
        Database,
        Historical
    }
}

Then the code that uses the SearchMemberEligibilityWrapper checks for the value of ResultSuccessful to decide what to do after the search attempt. You have three states that the search result can exist in:

  • Search unsuccessful (error)
  • Search successful, 0 results
  • Search successful, >0 results

If you're concerned about having multiple errors per file, then using this pattern you can even have a List<string> property for your errors, and list your errors individually, rather than simply having one error for everything.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.