J Fabian Meier
J Fabian Meier

Reputation: 35795

Initialize IEnumerable<int> as optional parameter

I have an optional parameter of type IEnumerable<int> in my C# method. Can I initialize it with anything but null, e.g. a fixed list of values?

Upvotes: 13

Views: 15337

Answers (6)

Steve Czetty
Steve Czetty

Reputation: 6228

I have been using the accepted answer forever, but I just had a brainstorm, and I think it would be interesting to share.

Using the semantics of default() with a struct, you can do this:

public readonly struct EmptyEnumerable<T> : IEnumerable<T>
{
    public IEnumerator<T> GetEnumerator()
    {
        return Enumerable.Empty<T>().GetEnumerator();
    }

    public IEnumerable.GetEnumerator()
    {
         return GetEnumerator();
    }
}

// ...elsewhere...

public void Foo<T>(IEnumerable<T> bar = default(EmptyEnumerable<T>))
{
}

The reason this works is because the default of a struct is not null, but is an instance of the struct as if it was called with the default constructor. Since default is determined at compile time, the compiler is happy with it, and it just works.

Of course, you can change the contents of GetEnumerator to return whatever you'd like, not just Enumerable.Empty, this is just my use-case.

Disclaimer

I do not actually use this in my code, for fear of issues caused by boxing. I just wanted to post this as an interesting exploration of how C# works. Maybe there's a way to solve the boxing issue, if I think of one, I'll come back here and update.

Upvotes: 0

V4Vendetta
V4Vendetta

Reputation: 38200

Well since you need compile time constants you would have to set it to null

but then you can do the following in your method

 list = list ?? new List<int>(){1,2,3,4};

Upvotes: 3

Tim Schmelter
Tim Schmelter

Reputation: 460078

No, you need a compile time constant.

But you could use an overload as work around:

public void Foo(int arg1)
{
      Foo(arg1, new[] { 1, 2, 3 });
}

public void Foo(int arg1, IEnumerable<int> arg2)
{
      // do something
}

Upvotes: 3

Nasmi Sabeer
Nasmi Sabeer

Reputation: 1380

How about making its default value as null, and within the method

numbers = numbers ?? Enumerable.Empty<int>();

or

numbers = numbers ?? new []{ 1, 2, 3}.AsEnumerable();

Upvotes: 4

Ilya Ivanov
Ilya Ivanov

Reputation: 23626

No. You can only have compile time constants. You can assign in to null and then

void SomeMethod(IEnumerable<int> list = null)
{
    if(list == null)
        list = new List<int>{1,2,3};
}

Next code snippet is take from well-known C# in Depth book by Jon Skeet. Page 371. He suggest to use null as kind of not set indicator for parameters, that may have meaningful default values.

static void AppendTimestamp(string filename,
                            string message,
                            Encoding encoding = null,
                            DateTime? timestamp = null)
{
     Encoding realEncoding = encoding ?? Encoding.UTF8;
     DateTime realTimestamp = timestamp ?? DateTime.Now;
     using (TextWriter writer = new StreamWriter(filename, true, realEncoding))
     {
         writer.WriteLine("{0:s}: {1}", realTimestamp, message);
     }
}

Usage

AppendTimestamp("utf8.txt", "First message");
AppendTimestamp("ascii.txt", "ASCII", Encoding.ASCII);
AppendTimestamp("utf8.txt", "Message in the future", null, new DateTime(2030, 1, 1));

Upvotes: 16

Matthew Watson
Matthew Watson

Reputation: 109567

No - default parameters must be compile-time constants.

Your best bet is to overload the method. Alternatively, set the default value to null and inside your method detect a null and turn it into the list you want.

Upvotes: 4

Related Questions