Initialize elements with brackets like Lists in c#
Asked Answered
J

4

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.

Judas answered 13/9, 2018 at 12:52 Comment(1)
You could inherit from List<int> insteadMarcimarcia
B
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} };
Bighorn answered 13/9, 2018 at 12:56 Comment(0)
M
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.

Marchetti answered 13/9, 2018 at 12:54 Comment(4)
Looks like you have to implement IEnumerable as well.Iceman
@PavelTupitsyn Yes, of course. Not sure how I missed that.Marchetti
It does not have to be generic, by the way :)Iceman
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. @PavelTupitsynMarchetti
N
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();
    }
  }
Nephritis answered 13/9, 2018 at 12:56 Comment(1)
@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.Nephritis
S
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};

Soiree answered 13/9, 2018 at 13:8 Comment(2)
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.Soiree
Sure, but still nice to know about it, even if it comes with the advice not to use it.Judas

© 2022 - 2024 — McMap. All rights reserved.