1

I'm new to C# and have been struggling to find an idiomatic way to initialize a list within a constructor.

Related questions that don't quite solve the issue:

This works, but with a flaw:

class Datapoint
{
    public bool Debug { get; set; }
    public string Pattern { get; private set; }

    // I would prefer to initialize this list in the constructor
    public List<Dictionary<string, dynamic>> operations = 
            new List<Dictionary<string, dynamic>>();

    // constructor
    public Datapoint(bool debug = false, 
                string pattern = ""
                // I would prefer operations to go here 
                // but the following doesn't work:
                // List<Dictionary<string, dynamic>> operations = 
                //     new List<Dictionary<string, dynamic>>()
                ) 
    {
        Debug = debug;
        Pattern = pattern;
    }
}


// Let's define some Datapoints
class Definitions 
{
    public static Datapoint turtles = new Datapoint
    (
        pattern: @"turtle pattern",
        // I would prefer operations to go here
    )
    {
        operations =
                { new Dictionary<string, dynamic>
                    {
                        ["func"] = "stitch_lines"
                    }
                }
    };
}

The flaw is that I cannot set operations as private, otherwise I get an error when creating turtles.

Ideally I would like operations to be a parameter of the constructor, but I am missing something as every combination I try yields this error:

Default parameter value for operations must be a compile-time constant.

Thanks in advance for any insights.

Community
  • 1
  • 1
droptable
  • 171
  • 1
  • 8
  • You can take in `List> operations = null` and then check for `null` in the constructor. – zimdanen Nov 10 '15 at 22:51
  • @zimdanen If you want to post that as a solution I'll accept it, otherwise let me know and I'll write it up in case someone else has the same problem. – droptable Nov 10 '15 at 23:07

3 Answers3

2

You can take in a null and then check for it in your constructor:

public Datapoint(
    bool debug = false, 
    string pattern = "",
    List<Dictionary<string, dynamic>> operations = null
)
{
    Debug = debug;
    Pattern = pattern;
    this.operations = operations ?? new List<Dictionary<string, dynamic>>();
}

However, see the comments of D Stanley's answer for a discussion of drawbacks in the general case.

zimdanen
  • 5,508
  • 7
  • 44
  • 89
1

As the error indicates, default values must be compile-time constants. I would do this as two overloads:

// constructor
public Datapoint(bool debug = false, 
            string pattern = "")
{
    Debug = debug;
    Pattern = pattern;

    operations = new List<Dictionary<string, dynamic>>();
}

public Datapoint(List<Dictionary<string, dynamic>> operations,
            bool debug = false, 
            string pattern = "")
{
    Debug = debug;
    Pattern = pattern;

    this.operations = operations;
}

Note that you need to reorder the parameters in the second overload since optional parameters must go at the end of the parameter list.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • Thanks for your reply. :) The issue with this (unless I'm missing something) is that I still have to instantiate `turtles` the same way, so I cannot set `operations` as private. – droptable Nov 10 '15 at 22:43
  • Yeah I didn't understand your question at first. See my edits. You can make it private if you have it as a constructor parameter. If you want it to be optional just have two overloads. – D Stanley Nov 10 '15 at 22:46
  • Thanks. Your new line `List>() operations)` is one of the constructs I had tried, but VS2015 gives me a lot of squigglies with it. – droptable Nov 10 '15 at 22:51
  • Check my edits again - I had an extra `()` and optional parameters must go at the end. – D Stanley Nov 10 '15 at 22:54
  • FYI, tried the suggestion by @zimdanen and that worked for me. Thank you for your kind help all the same. – droptable Nov 10 '15 at 22:59
  • @droptable Well, I'm glad it works, but I generally don't like using `null` as a "magic" value to mean something else - it's misleading and prevents you from legitimately setting the property value to `null`. But whatever works for you... – D Stanley Nov 10 '15 at 23:01
  • To be clear, the reason that solution works better is that the actual constructor has many optional parameters, so positional parameters are a problem -- I need to work with named params. – droptable Nov 10 '15 at 23:06
  • @DStanley: I don't see it as a magic value. The caller is not providing a value (because they don't have a value to provide), and the constructor is taking in null (which indicates that there is no value, which is what null is for). It's up to the class to determine what to do with a null value - which is the same decision that needs to be made in your example, but you have two constructors for it, and my suggestion has just the one. – zimdanen Nov 10 '15 at 23:09
  • 1
    @zimdanen I get it, but the drawback is that there's _no way to pass `null` as a legitimate value_. It may not be a problem in this case, but it is a problem when using optional parameters this way (and one reason why I prefer overloads to optional parameters in general). – D Stanley Nov 10 '15 at 23:11
  • @DStanley: I see and agree with your point in the general case. However, in this specific case, null is not a valid value, so my suggestion enforces that behavior. – zimdanen Nov 10 '15 at 23:12
1

Avoid using optional parameters this way

//constructor
public Datapoint(
    bool debug = false, //dangerous
    string pattern = "",//dangerous
    List<Dictionary<string, dynamic>> operations = null
)

Instead use:

//constructor
public Datapoint(
    bool? debug = null, 
    string pattern = null,
    List<Dictionary<string, dynamic>> operations = null
)
{
    Debug = debug.HasValue && debug.Value;
    Pattern = pattern;
    this.operations = (operations == null) ? 
              new List<Dictionary<string, dynamic>>() 
              : operations ;
}

Source: C# In Depth – Optional Parameters and Named Arguments

AiApaec
  • 660
  • 1
  • 6
  • 12