10

I was thinking about arrays and lists and wondering if and how classes can get an implementation to be initializable like them. Let's take this class as basis:

class TestClass
{
    private List<int> Numbers = new List<int> ();

    // Insert code here
}

What I would like to be able to do is to take the given values and internally fill my list.

TestClass test = new TestClass () { 2, 4, 7, 10 };

What would be normally possible is this:

List<int> test = new List<int> () { 2, 4, 7, 10 };

But I would like to have it for my own custom classes. The next question, which is optional, is if the same can be done for this syntax:

TestClass test = { 2, 4, 7, 10 };

I assume that is less likely to be possible. Note, these are different from this:

Cat cat = new Cat() { Name = "Sylvester", Age=8 };

These are direct and optional declarations of internal fields and properties.

See also MSDN for more info.

1
  • 1
    You could inherit from List<int> instead Commented Sep 13, 2018 at 12:56

4 Answers 4

13

As stated here, collection initialisers will work on any class that implements IEnumerable and has an Add() method.

class TestClass : IEnumerable<int>
{
    private List<int> Numbers = new List<int> ();

    public void Add(int n)
    {
        Numbers.Add(n);
    }

    // implement IEnumerable methods down here ...

    public IEnumerator<int> GetEnumerator() => Numbers.GetEnumerator();

    // etc ...
}

Note that if you have more than one parameter in your Add() method signature, then you can enclose them in curly braces to use collection initialiser syntax.

class TestClass2 : IEnumerable<(int, int)>
{
    private List<int> Numbers = new List<(int, int)> ();

    public void Add(int x, int y)
    {
        Numbers.Add((x, y));
    }

    // implement IEnumerable methods down here ...

    public IEnumerator<(int, int)> GetEnumerator() => Numbers.GetEnumerator();

    // etc ...
}

Would be used like this:

var test = new TestClass2 { {2, 3}, {4, 5} };
Sign up to request clarification or add additional context in comments.

Comments

8

You just need an Add method to make this work and implement the IEnumerable interface:

class TestClass : IEnumerable<int>
{
    private List<int> Numbers = new List<int> ();

    public void Add(int n)
    {
        Numbers.Add(n);
    }

    public IEnumerator<int> GetEnumerator()
    {
        return Numbers.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

The compiler will recognize the collection initializer syntax and will try to find a matching Add method.

4 Comments

Looks like you have to implement IEnumerable as well.
@PavelTupitsyn Yes, of course. Not sure how I missed that.
It does not have to be generic, by the way :)
No, it is better that way since you know the type after all. In that way you help LINQ too. Else you would need to Cast<int>() first. @PavelTupitsyn
4

Technically, the minimum requirements are

  1. IEnumerable interface (of course, IEnumerable<T> is a better choice)
  2. Add method

Minimum example (demonstration only):

  class TestClass: System.Collections.IEnumerable {
    public void Add(int value) { }

    public IEnumerator GetEnumerator() {
      // Just to demonstrate, do not do this in production code
      return null;
    }
  }

  ...

  TestClass myTest = new TestClass() {1, 2, 3};  

In your case:

  class TestClass: IEnumerable<int> {
    private List<int> Numbers = new List<int>();

    // This is required
    public void Add(int value) {
      Numbers.Add(value);
    }

    public IEnumerator<int> GetEnumerator() {
      return Numbers.GetEnumerator();
    }

    // This is required
    IEnumerator IEnumerable.GetEnumerator() {
      return GetEnumerator();
    }
  }

1 Comment

@laptou: yes, I totally agree with you that returning null is a nasty code; as well as implementing System.Collections.IEnumerable without IEnumerable<T>. I've olny tried to show the minimum requirements.
1

To answer your second subquestion: if it is possible to do

TestClass test = { 2, 4, 5, 7 };

I don't think this is correct syntax anywhere. However you could create something that looks like it using the implicit operator.

Since you can instantiate an int array like this: new[] {1,2,3,4}, you can create the following in your TestClass.

public class TestClass {
   private IEnumerable<int> list;
   public TestClass(IEnumerable<int> _list) {
       this.list = _list;
   }

   public static implicit operator(int[] input) {
        return new TestClass(input.ToList());      
   }
}

Then create a new TestClass like this; TestClass testClass = new[] {1,2,3};

2 Comments

This is pretty funky though and probably not a good idea in general. Better to just use the constructor. It only saves you a couple of braces and keywords, which is really not worth much.
Sure, but still nice to know about it, even if it comes with the advice not to use it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.