0

I am aware that in C# const can only be initialized at compile time. Which is related to what I am trying to do.

My objective is to write a constant array of constant objects in C#. How can I do that?

In order to explain my self, allow me to insert some code in C++ (I wrote some code some time ago) that explains what I am trying to do.

(In C++)

struct FD{
 string name; 
 double CE[2]; 
};

const FD models[2]={
     {"one",{2.0,1.0}},
     {"two",{3.0,4.0}}
};

Seems very simple, right? How can I do something like this in C#?


EDIT: (Attempted answer)

I found that you can do something like this in C#

 struct FD
    {
        public string name;
        public double[] CE;
    }
 static readonly FD[] models= new[]
   {
     new FD(){name="one" , CE= new double[]{1.0,2.0 } },
     new FD(){name="two", CE= new double[]{3.0,4.0}}         
   };

I wonder if this could be a good way to achieve my objective?

KansaiRobot
  • 7,564
  • 11
  • 71
  • 150
  • 4
    The closest would probably be a [readonly](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/readonly) [ReadOnlyCollection](https://msdn.microsoft.com/library/ms132474.aspx) of immutable structs/classes. But that's runtime and not compile time. – Corak Jul 20 '18 at 05:26
  • @Corak I found something like https://stackoverflow.com/a/309540/4451521 but it seems that is done on the heap not the stack and well it is not constant – KansaiRobot Jul 20 '18 at 05:30
  • @KansaiRobot - yes, that's still fully mutable. You can try `struct FD { public FD(string name, IEnumerable ce) { Name = name; CE = new ReadOnlyCollection(new List(ce)); } private readonly string Name; private readonly IReadOnlyList CE; }` -- but with that you can't restrict `CE` to only have exaclty two items. – Corak Jul 20 '18 at 05:38
  • @Corak Thanks. Could you take a look at my edit? – KansaiRobot Jul 20 '18 at 05:39
  • 2
    In your code, the instances of `FD` are fully mutable (try `models[0].name = "three";`. and the contents of the array `models` can also be changed freely (try `models[0] = new FD[...]`). -- *only* re-assigning a completely different array to the variable `models` is prohibited. – Corak Jul 20 '18 at 05:41
  • Maybe it helps if you explain why you need this? Could be possible that there is another solution. – royalTS Jul 20 '18 at 05:42
  • @royalTS in the program there are values that are constant through the application (and they have a structure). These values are going to be constantly compared with data that is dynamically produced – KansaiRobot Jul 20 '18 at 05:49
  • ... btw. in my suggestion of how to implement `FD`, the fields `Name` and `CE` should of course be **public**... – Corak Jul 20 '18 at 05:50
  • @Corak I am trying your suggestion. how do you initialize FD with your implementation?...Oh..I got it :) – KansaiRobot Jul 20 '18 at 06:02
  • 1
    @KansaiRobot - `new FD("one", new[] { 1.0, 2.0 })` -- or the complete `static readonly IReadOnlyList models = new ReadOnlyCollection(new List { new FD("one", new[] { 1.0, 2.0 }), new FD("two", new[] { 3.0, 4.0 }) });` – Corak Jul 20 '18 at 06:04
  • @Corak I tried it with the public and you can not change name, but you can change the content of model[0] with a new element. I will try a little more and if not that seems good enough – KansaiRobot Jul 20 '18 at 06:13
  • 1
    @KansaiRobot - Use `readonly IReadOnlyList models = new ReadOnlyCollection([...]);` as shown above. Then modifying `models` should not be allowed. – Corak Jul 20 '18 at 06:15
  • Just some light reading - [mutable structs](https://stackoverflow.com/questions/441309/why-are-mutable-structs-evil) – Alexei Levenkov Jul 20 '18 at 06:22
  • (Even less related/welcoming) - I don't think any language allocates process wide constants on the stack... I'm really not sure why you are concerned about " that is done on the heap not the stack"... – Alexei Levenkov Jul 20 '18 at 06:26
  • @KansaiRobot Would it then be possible to create a public static comparison method which has the constant values only internally? – royalTS Jul 20 '18 at 13:01
  • @royalTS maybe, but it lacks elegance. The idea is to have the values in an array of structures so that the values are completely separated from other code – KansaiRobot Jul 20 '18 at 13:13

3 Answers3

2

Sadly in C# you cannot define arrays or structures as const since the constructor can have runtime logic and therefore has to be executed at runtime. Your proposed solution is almost what you want, you just need a ReadonlyCollection<FD> to encapsulate your Array:

using System.Collections.ObjectModel;

static readonly ReadOnlyCollection<FD> models = new ReadOnlyCollection<FD>(new[]
{
    new FD(){name="one" , CE= new double[]{1.0,2.0 } },
    new FD(){name="two", CE= new double[]{3.0,4.0}}
});

Now you cannot change the reference of models (readonly) nor can you change the contents of it (ReadonlyCollection). You also cannot change any members of the FD instances since FD is a struct and the ReadonlyCollection creates another layer of indirection, basically copying your struct:

models = new ReadOnlyCollection<FD>(new FD[] { }); // Error CS0198  A static readonly field cannot be assigned to
models[0] = new FD(); // Error CS0200  indexer  is read only
models[0].name = "testname"; // Error CS1612  Cannot modify the return value

EDIT : I overlooked something, you can still change the array:

 models[0].CE[0] = 1; // compiles just fine

If possible you want to make CEreadonly too, but I'm not sure if your API allows that

Freggar
  • 1,049
  • 1
  • 11
  • 24
0

Thinking outside the box :P

Others are providing you with great ideas but if you want to have truly const 4 values then you could just:

    const double FD_one_0 = 2.0;
    const double FD_one_1 = 1.0;
    const double FD_two_0 = 3.0;
    const double FD_two_1 = 4.0;
tymtam
  • 31,798
  • 8
  • 86
  • 126
0

In the end thanks to users Corak and Freggar I could come with something like this. (I suppose that model3 is the answer

Structs

 struct FD
    {
        public string name;
        public double[] CE;
    }

    struct FD2
    {
        public FD2(string name, IEnumerable<double>ce)
        {
            Name = name;
            CE = new ReadOnlyCollection<double>(new List<double>(ce));
        }
        public readonly string Name;   
        public readonly IReadOnlyList<double> CE;
    }

and the lists

 //This solution does not give the entire constant structure as name and FD[0] can be changed
        static readonly FD[] models= new[]
          {
              new FD(){name="one" , CE= new double[]{1.0,2.0 } },
              new FD(){name="two", CE= new double[]{3.0,4.0}}

          };

        //Here name can not be changed but models2[0] can be
        static readonly FD2[] models2 = new[]
        {
            new FD2("one",new double[]{1.0,2.0 }),
            new FD2("two", new double[]{3.0,4.0 })
        };

        //This is the best solution
        static readonly IReadOnlyList<FD2> models3 = new ReadOnlyCollection<FD2>(new[] {
            new FD2("one",new double[]{1.0,2.0 }),
            new FD2("two", new double[]{3.0,4.0 })
        }
        );

        //This is also a good solution but models4[0].CE[0] can be changed
        static readonly ReadOnlyCollection<FD> models4 = new ReadOnlyCollection<FD>(new[]
        {
             new FD(){name="one" , CE= new double[]{1.0,2.0 }},
             new FD(){name="two", CE= new double[]{3.0,4.0}}
        }); 

I guess models3 gives a good implementation to what I wanted to achieve.

KansaiRobot
  • 7,564
  • 11
  • 71
  • 150