1

I have a class which looks like this:

public class Grid<T>
{
    public T[] gridArray;


    public Grid(int size)
    {
        gridArray = new T[size];

        gridArray.FillArray<T>();
    }

}

I'd like my gridArray.FillArray<T>(); method to fill the gridArray with -1, if it's of type int and to fill it with false, if it's of type bool.

How can i achieve this?

Tobias Weger
  • 105
  • 8
  • 3
    `default(T)` will give you `0` when `T` is `int`, and `false` when `T` is `bool`. There's no *good* way to get `-1` for `int` in a generic fashion. – p.s.w.g Mar 08 '19 at 21:26

2 Answers2

2

I can see two concerns in this question

How to create an array with non-default prefilled values?

There is no clean way to fill a generic array with custom default value, except for creating it manually.

You might want to read this StackOverflow question too, because it is related to this topic:
What is the equivalent of memset in C#?

Taking this into account, you can define your Grid class as the following:

public class Grid<T>
{
    private T[] _gridArray;  

    public Grid(int size)
    {
        _gridArray = new T[size];
    }            

    public Grid(int size, T initialValue)
    {
        _gridArray = new T[size];
        for (int i = 0; i < size; i++)
            _gridArray[i] = initialValue;

        // or it can be rewritten with LINQ, if you like:
        // _gridArray = Enumerable.Repeat(initialValue, size).ToArray();
    }    
}

Now, you can use it to create default arrays:

var grid = new Grid<int>(3); // [0, 0, 0]
var grid = new Grid<object>(3); // [null, null, null]
var grid = new Grid<bool>(4); // [false, false, false, false]

but you can also provide a value which you would like to be as a default:

var grid = new Grid<int>(3, -1); // [-1, -1, -1]
var grid = new Grid<bool>(4, true); // [true, true, true, true]

How to set custom default values per generic T

If you want -1 to be default value for all Grid<int>, you can create a factory, which will know about it and create this according to this logic. This factory can store default values for each type and validate your T against them.

Of course, you could put this logic into constructor just like you initially wanted it to be:

public class Grid<T>
{
    private static readonly Dictionary<Type, object> _customDefaultValues = new Dictionary<Type, object>
    {
        [typeof(int)] = -1,
        [typeof(long)] = long.MaxValue
    };

    public T[] _gridArray;  

    public Grid(int size)
    {
        _gridArray = new T[size];

        if (_customDefaultValues.TryGetValue(typeof(T), out object defaultValue))
        {
            T defaultValueUnboxed = (T)defaultValue;
            for (int i = 0; i < size; i++)
                _gridArray[i] = defaultValueUnboxed;
        }
    }  
}

var grid = new Grid<int>(4); // [-1, -1, -1, -1]
var grid = new Grid<long>(2); // [long.MaxValue, long.MaxValue]
var grid = new Grid<bool>(3); // [false, false, false]

but for me it looks a little bit sloppy and non-transparent. Also, it creates new instance of static _customDefaultValues for each create closed generic type. That's why it would be better to extract it to a factory class.

public class Grid<T>
{
    private T[] _gridArray;  

    public Grid(int size)
    {
        _gridArray = new T[size];
    }            

    public Grid(int size, T initialValue)
    {
        _gridArray = new T[size];
        for (int i = 0; i < size; i++)
            _gridArray[i] = initialValue;
    }    
}

public static class GridFactory
{
    private static readonly Dictionary<Type, object> _customDefaultValues = new Dictionary<Type, object>
    {
        [typeof(int)] = -1,
        [typeof(long)] = long.MaxValue
    };

    public static Grid<T> Create<T>(int size)
    {
        if (_customDefaultValues.TryGetValue(typeof(T), out object defaultValue))
            return new Grid<T>(size, (T)defaultValue);

        return new Grid<T>(size);
    }        
}

It results into more code, but this way it looks more clear:

var grid = GridFactory.Create<int>(3); // [-1, -1, -1]
var grid = GridFactory.Create<long>(4); // [long.MaxValue, long.MaxValue, long.MaxValue, long.MaxValue]
var grid = GridFactory.Create<object>(3); // [null, null, null] by default
Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
1

Looks like you want to create an extension method for this. One option is to use overloading and implement separate method for every type you need

public static class Ext
{
    public static void FillArray(this int[] array)
    {
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = -1;
        }
    }

    public static void FillArray(this bool[] array)
    {
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = false;
        }
    }
}

It's possible to use this approach when you explicitely create int or bool array

var arr = new int[size];
arr.FillArray();

And it is not possible to use this approach with generic parameter like you do in Grid. So another option would be creating generic extension method with checking type in runtime

public static class Ext
{
    public static void FillArray<T>(this T[] array)
    {
        Type itemType = typeof(T);

        T value;
        if (itemType == typeof(bool))
        {
            value = (T)Convert.ChangeType(false, itemType);
        }
        else if (itemType == typeof(int))
        {
            value = (T)Convert.ChangeType(-1, itemType);
        }
        else
        {
            value = default(T);
        }

        for (int i = 0; i < array.Length; i++)
        {
            array[i] = value;
        }
    }
}

Note

Consider the fact that when you instantiate an array (new type[size]) it is already filled by default value of its type (0 for int and false for bool). So possibly you won't need these extension method if default values are okay for you.

Alexander
  • 9,104
  • 1
  • 17
  • 41