initonly array member as an lvalue

L

lee.crabtree

In C#, an array marked 'readonly', as in:

public readonly bool[] array = new bool[8];

would allow individual members of the array to be modified while
protecting the array reference itself from being changed. That is, I
could do this:

array[0] = false;

but not this:

array = null;

However, it doesn't seem like the 'initonly' keyword in C++/CLI does
exactly the same thing. If I do something like this:

public ref class Thing
{
public:
Thing();
void SetBool();
protected:
initonly array<bool>^ bArr;
};

Thing::Thing()
{
bArr = gcnew array<bool>(8);
}

void Thing::SetBool()
{
bArr[0] = false;
}

I get a compilation error about using an 'initonly' field as an l-
value. What's the right way to go about this?
 
M

Mark Salsbery [MVP]

Your initialization needs to be done in the constructor, even for items in
the array.

Mark
 
L

lee.crabtree

Even for an array of value types? That seems strange. That still
doesn't explain why the items in the array can't be used as an l-
value.
 
M

Mark Salsbery [MVP]

Here's the error on VS2008:

"error C3893: 'Thing::bArr' : l-value use of initonly data member is only
allowed in an instance constructor of class 'Thing'"

l-value refers to using the variable as the left-value in an assignment -
this is not allowed outside the constructor for initonly variables.

Mark
 
L

lee.crabtree

Right, my point is that it doesn't make sense that you can't then
modify entries IN the array. The C# version of 'initonly', which is
'readonly', merely makes reassigning the REFERENCE illegal. In
effect, you can't re-new the array or set its reference to null, but
you can set and get entries IN the array.

Am I assuming that 'initonly' should function like 'readonly'?
 
M

Mark Salsbery [MVP]

I'm not sure why you think C++ initonly should act like C# readonly.

They aren't even the same keyword.

initonly in C++ does what it says - lets you initialize it and that's it.

Mark
 
L

lee.crabtree

I guess the only reason I think 'initonly' and 'readonly' should act
alike is that I found about a dozen articles online stating that they
did. Serves me right for looking it up, I suppose.
 
M

Mark Salsbery [MVP]

Interesting ... I looked up the readonly keyword for C#. It does indeed
sounds like it should work the same.

Is it a C# bug that allows you to alter members of an array outside a
constructor?

What do the C# experts have to say about that? :)

Mark
 
L

lee.crabtree

I think it's more likely that the association of 'initonly' to
'readonly' is erroneous in some regard. As far as I can tell,
'readonly' is only supposed to prevent the REFERENCE from being
overwritten. In that sense, a List<> marked 'readonly' will allow
adding, removing, and clearing from the list, but setting the list to
null or trying to re-new it would be an error. In that sense, it's
similar to what I remember (from my long-past days of C++) a 'const'
pointer being used for. The VALUE is changeable, it's the REFERENCE
that isn't.
 
M

Mark Salsbery [MVP]

Right. I see what you mean, but from the readonly (C#) docs:

"...assignments to the fields introduced by the declaration can only occur
as part of the declaration or in a constructor in the same class."

and

"The readonly keyword is different from the const keyword. A const field can
only be initialized at the declaration of the field. A readonly field can be
initialized either at the declaration or in a constructor. Therefore,
readonly fields can have different values depending on the constructor
used."

That reads to me just like initonly and const in C++. That's why I wonder
if C# should allow you to change readonly variables later.

It doesn't read like it should just apply to the reference.

Mark
 
B

Ben Voigt [C++ MVP]

Mark Salsbery said:
Right. I see what you mean, but from the readonly (C#) docs:

"...assignments to the fields introduced by the declaration can only occur
as part of the declaration or in a constructor in the same class."

He isn't assigning to the field, he's performing member access on the object
referenced by the field.

It's like having

class C
{
bool* const p; // p must be assigned in constructor, p[0] can be
assigned anywhere
};


The workaround would be:

ref class C
{
initonly array<bool>^ b;

C() : b(gcnew array<bool>(8)) {}
void doit() { /* b[0] = false; */ array<bool>^ bprime; bprime[0] =
false; }
};
 
B

Ben Voigt [C++ MVP]

Ben Voigt said:
Mark Salsbery said:
Right. I see what you mean, but from the readonly (C#) docs:

"...assignments to the fields introduced by the declaration can only
occur as part of the declaration or in a constructor in the same class."

He isn't assigning to the field, he's performing member access on the
object referenced by the field.

It's like having

class C
{
bool* const p; // p must be assigned in constructor, p[0] can be
assigned anywhere
};


The workaround would be:

ref class C
{
initonly array<bool>^ b;

C() : b(gcnew array<bool>(8)) {}
void doit() { /* b[0] = false; */ array<bool>^ bprime; bprime[0] =
false; }
};

oops, missed the initialization of bprime, should have been

void doit() { /* b[0] = false; */ array<bool>^ bprime = b; bprime[0] =
false; }
 
L

lee.crabtree

I can't believe I didn't think to do that from the beginning. Even
so, this seems like really strange behavior.
 
M

Mark Salsbery [MVP]

Right. You can get around const with casts too.
But why would const or initonly be used on variables one wants to change
later?

I guess I missed the original point of the thread.
My point was just that initonly works as documented.

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++



Ben Voigt said:
Ben Voigt said:
Mark Salsbery said:
Right. I see what you mean, but from the readonly (C#) docs:

"...assignments to the fields introduced by the declaration can only
occur as part of the declaration or in a constructor in the same class."

He isn't assigning to the field, he's performing member access on the
object referenced by the field.

It's like having

class C
{
bool* const p; // p must be assigned in constructor, p[0] can be
assigned anywhere
};


The workaround would be:

ref class C
{
initonly array<bool>^ b;

C() : b(gcnew array<bool>(8)) {}
void doit() { /* b[0] = false; */ array<bool>^ bprime; bprime[0] =
false; }
};

oops, missed the initialization of bprime, should have been

void doit() { /* b[0] = false; */ array<bool>^ bprime = b; bprime[0] =
false; }
 
B

Ben Voigt [C++ MVP]

Mark Salsbery said:
Right. You can get around const with casts too.
But why would const or initonly be used on variables one wants to change
later?

What cast? There is no cast.

The reference marked as initonly is not being changed, it always holds the
address of the same object. The data within that object could change. This
could, for example, allow a gc optimization because the lifetime of the two
objects are equal and therefore the objects needn't be tracked separately.

I guess I missed the original point of the thread.
My point was just that initonly works as documented.

Perhaps, but it functions differently than native C++ const or C# readonly,
and the behavior makes no sense. If an initonly reference is supposed to
point to an immutable object, then it should carry the initonly attribute on
the referred-to type and not be lost without a const_cast. It then would
look like this:

array<initonly bool>^ b;

But that makes no sense, because it raises the question "Which constructor
is allowed to change the value?"

If you have

array<bool>^ initonly b;

then it is only natural to be able to

b[0] = false;
Mark

--
Mark Salsbery
Microsoft MVP - Visual C++



Ben Voigt said:
Ben Voigt said:
message Right. I see what you mean, but from the readonly (C#) docs:

"...assignments to the fields introduced by the declaration can only
occur as part of the declaration or in a constructor in the same
class."

He isn't assigning to the field, he's performing member access on the
object referenced by the field.

It's like having

class C
{
bool* const p; // p must be assigned in constructor, p[0] can be
assigned anywhere
};


The workaround would be:

ref class C
{
initonly array<bool>^ b;

C() : b(gcnew array<bool>(8)) {}
void doit() { /* b[0] = false; */ array<bool>^ bprime; bprime[0] =
false; }
};

oops, missed the initialization of bprime, should have been

void doit() { /* b[0] = false; */ array<bool>^ bprime = b; bprime[0] =
false; }
 
M

Mark Salsbery [MVP]

Ben Voigt said:
What cast? There is no cast.

The reference marked as initonly is not being changed, it always holds the
address of the same object. The data within that object could change.
This could, for example, allow a gc optimization because the lifetime of
the two objects are equal and therefore the objects needn't be tracked
separately.



Perhaps, but it functions differently than native C++ const or C#
readonly,


Right. My original point was that comparing two different keywords from two
different languages was irrelevant, no matter how many articles or other
people say they are the same..

and the behavior makes no sense.


Now that I'm following the theme of the thread, I agree 100% :)

Thanks Ben!
Mark


If an initonly reference is supposed to point to an immutable object, then
it should carry the initonly attribute on the referred-to type and not be
lost without a const_cast. It then would look like this:

array<initonly bool>^ b;

But that makes no sense, because it raises the question "Which constructor
is allowed to change the value?"

If you have

array<bool>^ initonly b;

then it is only natural to be able to

b[0] = false;
Mark

--
Mark Salsbery
Microsoft MVP - Visual C++



news:[email protected]...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top