Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant initialization of an array of class instances in C# [closed]

Let's say I have a class like this:

public class Fraction {    int numerator;    int denominator;     public Fraction(int n, int d)    {       // set the member variables    }     // And then a bunch of other methods } 

I want to initialize an array of them in a nice way, and this post is a big list of approaches that are error prone or syntactically cumbersome.

Of course an array constructor would be nice, but there's no such thing:

public Fraction[](params int[] numbers) 

So I'm forced to use a method like

public static Fraction[] CreateArray(params int[] numbers) {     // Make an array and pull pairs of numbers for constructor calls } 

which is relatively clunky, but I don't see a way around it.

Both forms are error prone because a user could mistakenly pass an odd number of parameters, maybe because s/he skipped a value, which would leave the function scratching its head wondering what the user actually wanted. It could throw an exception, but then the user would need to try/catch. I'd rather not impose that on the user if possible. So let's enforce pairs.

public static Fraction[] CreateArray(params int[2][] pairs) 

But you can't call this CreateArray in a nice way, like

Fraction.CreateArray({0,1}, {1,2}, {1,3}, {1,7}, {1,42}); 

You can't even do

public static Fraction[] CreateArray(int[2][] pairs) // Then later... int[2][] = {{0,1}, {1,2}, {1,3}, {1,7}, {1,42}}; Fraction.CreateArray(numDenArray); 

Note that this would work just fine in C++ (I'm pretty sure).

You're forced to do one of the following instead, which is abhorrent. The syntax is terrible and it seems really awkward to use a jagged array when all the elements have the same length.

int[2][] fracArray = {new int[2]{0,1}, /*etc*/); Fraction.CreateArray(fracArray); // OR Fraction.CreateArray(new int[2]{0,1}, /*etc*/); 

Similarly, Python-style tuples are illegal and the C# version is icky:

Fraction.CreateArray(new Tuple<int,int>(0,1), /*etc*/); 

The use of a pure 2D array might take the following form, but it's illegal, and I'm sure there's no legal way to express it:

public static Fraction[] CreateArray(int[2,] twoByXArray) // Then later... Fraction[] fracArray =      Fraction.CreateArray(new int[2,4]{{0,1}, {1,2}, {1,3}, {1,6}}); 

This doesn't enforce pairs:

public static Fraction[] CreateArray(int[,] twoByXArray) 

OK, how about

public static Fraction[] CreateArray(int[] numerators, int[] denominators) 

But then the two arrays might have different lengths. C++ allows

public static Fraction[] CreateArray<int N>(int[N] numerators, int[N] denominators) 

but, well, this isn't C++, is it?

This sort of thing is illegal:

public static implicit operator Fraction[](params int[2][] pairs) 

and unworkable anyway, again because of the abhorrent syntax:

Fraction[] fracArray = new Fraction[](new int[2]{0,1}, /*etc*/ ); 

This might be nice:

public static implicit operator Fraction(string s) {     // Parse the string into numerator and denominator with     // delimiter '/' } 

Then you can do

string[] fracStrings = new string[] {"0/1", /*etc*/}; Fraction[] fracArray = new Fraction[fracStrings.Length]; int index = 0; foreach (string fracString in fracStrings) {     fracArray[index] = fracStrings[index]; } 

I don't like this approach for five reasons. One, the implicit cast unavoidably instantiates a new object, but we already have a perfectly good one, namely the one we're trying to initialize. Two, it can be confusing to read. Three, it forces you to do explicitly what I wanted to encapsulate in the first place. Four, it leaves room for bad formatting. Five, it involves one-time parsing of string literals, which is more like a practical joke than good programming style.

The following also requires wasteful instantiation:

var fracArray = Array.ConvertAll(numDenArray, item => (Fraction)item); 

The following use of a property has the same problem unless you use those terrible jagged arrays:

public int[2] pair {     set {         numerator = value[0];         denominator = value[1];     } } // Then later... var fracStrings = new int[2,4] {{0,1}, /*etc*/}; var fracArray = new Fraction[fracStrings.Length]; int index = 0; foreach (int[2,] fracString in fracStrings) {     fracArray[index].pair = fracStrings[index]; } 

This variation doesn't enforce pairs:

foreach (int[,] fracString in fracStrings) {     fracArray[index].pair = fracStrings[index]; } 

Again, this approach is big anyway.

These are all of the ideas I know how to derive. Is there a good solution?

like image 933
MackTuesday Avatar asked Apr 20 '16 07:04

MackTuesday


People also ask

What is the correct way of initializing an array in C?

Array Initialization Using a Loop The following syntax uses a “for loop” to initialize the array elements. This is the most common way to initialize an array in C. // declare an array. int my_array[5];

What is the syntax for array initialization?

Initializing an array In order to store values in the array, we must initialize it first, the syntax of which is as follows: datatype [ ] arrayName = new datatype [size];

How do you initialize all members of an array to the same value?

Initializer List: To initialize an array in C with the same value, the naive way is to provide an initializer list. We use this with small arrays. int num[5] = {1, 1, 1, 1, 1}; This will initialize the num array with value 1 at all index.

Does C initialize arrays to 0?

An array may be partially initialized, by providing fewer data items than the size of the array. The remaining array elements will be automatically initialized to zero.


2 Answers

I can't think of an elegant, and at the same time memory efficient solution for array.

But there is an elegant solution for list (and similar) utilizing the C# 6 collection initializer feature:

public static class Extensions {     public static void Add(this ICollection<Fraction> target, int numerator, int denominator)     {         target.Add(new Fraction(numerator, denominator));     } } 

With that extension method in place, you can easily initialize a Fraction list for instance:

var list = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } }; 

And of course, although not memory efficient, you can use it to initialize Fraction array either:

var array = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } }.ToArray(); 

or even making it more concise by declaring a list derived class with implicit array conversion operator:

public class FractionList : List<Fraction> {     public static implicit operator Fraction[](FractionList x) => x?.ToArray(); } 

and then use

Fraction[] array = new FractionList { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } }; 
like image 64
Ivan Stoev Avatar answered Sep 29 '22 17:09

Ivan Stoev


You could create a fraction array builder with a fluent interface. It would lead to something like

public class FractionArrayBuilder {   private readonly List<Fraction> _fractions = new List<Fraction>();    public FractionArrayBuilder Add(int n, int d)   {     _fractions.Add(new Fraction(n, d));     return this;   }    public Fraction[] Build()   {     return _fractions.ToArray();   } } 

which can be called using

var fractionArray = new FractionArrayBuilder()   .Add(1,2)   .Add(3,4)   .Add(3,1)   .Build(); 

which is an easy to understand statement.

I have made a fiddle to demonstrate.

like image 28
Andy Nichols Avatar answered Sep 29 '22 17:09

Andy Nichols