65.9K
CodeProject is changing. Read more.
Home

How to Add Collection Initialization Support in Classes

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (6 votes)

Apr 8, 2014

CPOL

1 min read

viewsIcon

14134

Make initialization more compact

Basics

C# 3.0 introduced object and collection initializers.

Object initializers can be used with any type and let assign values to any accessible fields or properties at creation time.

Collection initializers let you specify one or more element initializers when you initialize a collection class that implements IEnumerable. The element initializers can be a simple value, an expression or an object initializer. By using a collection initializer, you do not have to specify multiple calls to the Add method of the class in your source code; the compiler adds the calls. (MSDN)

Implementation

Where is no need in additional coding to implement object initializers: you get them out of box.

Collection initializers are meaningful under certain conditions. Every decent C# book gives examples of Array, List and sometimes Dictionary initialization. There is a simple receipt how to make your class support collection initialization if necessary.

Class has to implement:

  1. IEnumerable interface
  2. Accessible Add() method

Look at the example (a bit artificial, but working):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
 
namespace Demo
{
    public class Vector : IEnumerable<double>
    {
        private readonly int _length;
        private readonly double[] _coordinates;
        private readonly bool[] _readState;
        private int _index;
 
        public Vector(int length)
        {
            if (length <= 0)
                throw new ArgumentOutOfRangeException("length","Provide a positive value");
            _length = length;
            _coordinates = new double[_length];
            _readState = new bool[_length];
            _index = 0;            
        }
 
        public double this[int index]
        {
            get { return _coordinates[index]; }
            set
            {
                if (_readState[index])
                    throw new ReadOnlyException("Requested position is read-only");
                _coordinates[index] = value;
            }
        }
 
        public bool GetReadonly(int index)
        {
            return _readState[index];
        }
 
        public void SetReadonly(int index, bool value)
        {
            _readState[index] = value;
        }
 
        public IEnumerator<double> GetEnumerator()
        {
            return ((IEnumerable<double>) _coordinates).GetEnumerator();
        }
 
        public void Add(double value)
        {
            Add(value, false);
        }
 
        public void Add(double value, bool readState)
        {
            this[_index] = value;
            SetReadonly(_index, readState);
            _index++;
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

And here is the correct code which creates Vector object:

var vector = new Demo.Vector(3)
                    {
                        1,
                        {3.14, true},
                        -1,
                    };

Intellisense knows about this syntax and shows a hint:

Pay attention that both Add() methods can be used in the same statement.

Collection initializers are easy-to-use and compact-looking. The only obstacle is that either an object initializer or a collection initializer can be used at a time (according to C# specification).