15

I know this can be done using null so I have a workaround for that, but I was wondering if there was a better way that I can have an optional int[] parameter for a class?

class PriceLevels
{
    public int[] priceLevels { get; }
    private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels(int[] newPriceLevels = defaultPriceLevels)
    {
        priceLevels = newPriceLevels;
    }
}

This gives me an error saying it is an invalid expression defaultPriceLevels must be constant. How can I fix this?

One workaround that I can thing of is this, but I don't really like the solution

class PriceLevels
{
    public int[] priceLevels { get; }
    private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels(int[] newPriceLevels = null)
    {
        if(newPriceLevels == null) 
            priceLevels = defaultPriceLevels;
        else 
            priceLevels = newPriceLevels;
    }
}
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
Adjit
  • 10,134
  • 12
  • 53
  • 98
  • 3
    use overloading – kurakura88 Oct 11 '16 at 06:04
  • Possible duplicate of [Passing an empty array as default value of an optional parameter](http://stackoverflow.com/questions/3480382/passing-an-empty-array-as-default-value-of-an-optional-parameter) – Ralf Bönning Oct 11 '16 at 06:07
  • Why your `priceLevels` property has no setter? – Salah Akbari Oct 11 '16 at 06:08
  • Default Value by assignment for a function's input value can be the same as "const" assignment rules……So yours cannot be implemented directly until now. – xqMogvKW Oct 11 '16 at 06:12
  • 1
    @S.Akbari You're probably right. However, if the idea is to only allow `priceLevels` to be set on initialization (hence the default value as a fallback), then it's valid. – Michael McMullin Oct 11 '16 at 06:22
  • 1
    Possible duplicate of [Setting the default value of a C# Optional Parameter](http://stackoverflow.com/questions/2729639/setting-the-default-value-of-a-c-sharp-optional-parameter) – hatchet - done with SOverflow Oct 11 '16 at 20:20

6 Answers6

17

A better design all together would be to have 2 constructors (constructor overload) one that gets a int[] and another that doesn't:

class PriceLevels
{
    public int[] priceLevels { get; set; }
    private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels()
    {
        priceLevels = defaultPriceLevels;
    }

    public PriceLevels(int[] newPriceLevels)
    {
       priceLevels = newPriceLevels;
    }
}

If not, don't know if i'd call this "better" but you can use the params keyword:

class PriceLevels
{
    public int[] priceLevels { get; set; }
    private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels(params int[] newPriceLevels)
    {
        priceLevels = newPriceLevels.Length == 0 ? defaultPriceLevels : newPriceLevels;
    }
}

Also, depending on design, I'm not convinced that it is PriceLevels responsibility to decide what the default values are and maybe it should get it as a dependency in any case - See SOLID and Dependency Injection. Then you'd have only 1 constructor:

class PriceLevels
{
    public int[] priceLevels { get; set; }

    public PriceLevels(int[] newPriceLevels)
    {
       priceLevels = newPriceLevels;
    }
}
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
9

You can overload your constructor.

class PriceLevels
{
    public int[] priceLevels { get; private set; }
    private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels()
    {
        priceLevels = defaultPriceLevels;
    }

    public PriceLevels(int[] newPriceLevels)
    {
       priceLevels = newPriceLevels;
    }
}
jegtugado
  • 5,081
  • 1
  • 12
  • 35
7

You could create another constructor with no parameters that would just pass your defaultPriceLevels variable to the constructor you already have? You'd have to change the variable to static too.

e.g.

class PriceLevels
{
    public int[] priceLevels { get; }
    private static int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

    public PriceLevels(int[] newPriceLevels = null)
    {
        if (newPriceLevels == null) priceLevels = defaultPriceLevels;
        else priceLevels = newPriceLevels;
    }

    public PriceLevels() : this(defaultPriceLevels)
    { }
}
Mathew
  • 4,297
  • 4
  • 29
  • 38
7

Another option in the Crowd :), which is closest to your original code

class PriceLevels
{
  public int[] priceLevels { get; set; }

  private readonly int[] defaultPriceLevels = { 2, 3, 3, 4, 5, 6 };

  public PriceLevels(int[] newPriceLevels = null)
  {
     priceLevels = newPriceLevels ?? defaultPriceLevels;
  }
}
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
4

Just create an overload of the constructor! It can be really easily done with one line of code!

public PriceLevels() : this(defaultPriceLevels) { }

Then, remove the default parameter value from the original constructor:

public PriceLevels(int[] newPriceLevels)

Actually, you also need to declare defaultPriceLevels as static. Hopefully you don't mind that.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
3

Optional array parameters are not possible in C#. From MSDN

Each optional parameter has a default value as part of its definition. If no argument is sent for that parameter, the default value is used. A default value must be one of the following types of expressions:

  • a constant expression;
  • an expression of the form new ValType(), where ValType is a value type, such as an enum or a struct;
  • an expression of the form default(ValType), where ValType is a value type.

Therefore a "workaround" has to be used. Either check for null (as you suggested) or you can use a default constructor.

Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67