newbie trouble making array of instances

  • Thread starter Thread starter luke
  • Start date Start date
L

luke

I have a class 'item' and I want to create an array of these items.

like this

ArrayList items = new ArrayList();
while (reader.read())
{
item i = new item(Convert.ToString(reader[0]));
items.Add(i);
}

But the address of item (&item) is the same for each pass of the
loop. Consequentially every reference in the array is the same and
every item in my array displays the value of the last item set - which
makes sense considering all the references point to the same memory
location. Ideally I'd like to have an array of unique items. How do
I get a fresh instance on each pass of the loop?

Maybe I haven't had sleep enough? Usually I program in C++.

TIA
Luke
 
luke said:
I have a class 'item' and I want to create an array of these items.

like this

ArrayList items = new ArrayList();
while (reader.read())
{
item i = new item(Convert.ToString(reader[0]));
items.Add(i);
}

But the address of item (&item) is the same for each pass of the
loop. Consequentially every reference in the array is the same and
every item in my array displays the value of the last item set - which
makes sense considering all the references point to the same memory
location. Ideally I'd like to have an array of unique items. How do
I get a fresh instance on each pass of the loop?

Maybe I haven't had sleep enough? Usually I program in C++.

TIA
Luke

The code that you have shown _does_ create a new item in each iteration
of the loop.

&item is not the address of the item that you have created, &i is.
 
luke said:
I have a class 'item' and I want to create an array of these items.

like this

ArrayList items = new ArrayList();
while (reader.read())
{
item i = new item(Convert.ToString(reader[0]));
items.Add(i);
}

But the address of item (&item) is the same for each pass of the
loop.

Well, "item" is a class (not an instance), so it should have a fixed
address. I think that you mean &i, not &item. But the variable i can still
have the same adress for every pass of the loop, since it is a local
variable in the stack. Since you usually program in C++, think of i as a
pointer (that is, in C++ terms, i would be an item*, even though in C# you
just declare it as "item" instead of "item*"). The pointer has a fixed
address, even if the content of the pointer (the address to which it points)
is different for every iteration. Or in C++ terms, &i is constant even
though *i varies.
Consequentially every reference in the array is the same and
every item in my array displays the value of the last item set - which
makes sense considering all the references point to the same memory
location. Ideally I'd like to have an array of unique items. How do
I get a fresh instance on each pass of the loop?

It should be working the way you describe. The only reason why you
wouldn't be getting a different item on each iteration of the loop is if the
call to "new item" is always returning the same reference. Take a look at
what you are returning from the constructor in the item class.
 
Alberto said:
The only reason why you
wouldn't be getting a different item on each iteration of the loop is if
the call to "new item" is always returning the same reference. Take a
look at what you are returning from the constructor in the item class.

When using the new keyword, you always get a new instance. The instance
is created before the code in the constructor is executed, the
constructor doesn't return the instance.
 
When using the new keyword, you always get a new instance.

<trivia>
Nearly, at least. There's an oddity with the string class is you pass
in an empty array of chars:

using System;

class Test
{
static void Main(string[] args)
{
string x = new string(new char[]{});
string y = new string(new char[]{});
Console.WriteLine(object.ReferenceEquals(x,y));
}
}
</trivia>

But that's the only example I know of :)

Jon
 
Göran Andersson said:
When using the new keyword, you always get a new instance. The instance is
created before the code in the constructor is executed, the constructor
doesn't return the instance.

Oops... Sorry! I don't know what I was thinking. Or rather, yes, I know
what I was thinking: the Singleton pattern, where you always get the same
instance... but you get it from a function, not from the constructor. Sorry
about the hasty answer.
 
luke said:
I have a class 'item' and I want to create an array of these items.
like this
ArrayList items = new ArrayList();
while (reader.read())
{
item i = new item(Convert.ToString(reader[0]));
items.Add(i);
}
But the address of item (&item) is the same for each pass of the
loop. Consequentially every reference in the array is the same and
every item in my array displays the value of the last item set - which
makes sense considering all the references point to the same memory
location. Ideally I'd like to have an array of unique items. How do
I get a fresh instance on each pass of the loop?
Maybe I haven't had sleep enough? Usually I program in C++.

The code that you have shown _does_ create a new item in each iteration
of the loop.

&item is not the address of the item that you have created, &i is.

Thanks Göran - I meant &i
 
I have a class 'item' and I want to create an array of these items.
like this
ArrayList items = new ArrayList();
while (reader.read())
{
item i = new item(Convert.ToString(reader[0]));
items.Add(i);
}
But the address of item (&item) is the same for each pass of the
loop.

Well, "item" is a class (not an instance), so it should have a fixed
address. I think that you mean &i, not &item. But the variable i can still
have the same adress for every pass of the loop, since it is a local
variable in the stack. Since you usually program in C++, think of i as a
pointer (that is, in C++ terms, i would be an item*, even though in C# you
just declare it as "item" instead of "item*"). The pointer has a fixed
address, even if the content of the pointer (the address to which it points)
is different for every iteration. Or in C++ terms, &i is constant even
though *i varies.
Consequentially every reference in the array is the same and
every item in my array displays the value of the last item set - which
makes sense considering all the references point to the same memory
location. Ideally I'd like to have an array of unique items. How do
I get a fresh instance on each pass of the loop?

It should be working the way you describe. The only reason why you
wouldn't be getting a different item on each iteration of the loop is if the
call to "new item" is always returning the same reference. Take a look at
what you are returning from the constructor in the item class.
Maybe I haven't had sleep enough? Usually I program in C++.

Oh so 'i' is like a pointer and I am returning the address of the
pointer... In my watch window, if I don't use the ampersand it shows
the contents of my class. How do I get the value stored in the
pointer?

I don't think I'm returning anything from my constructor - it just
sets a bunch of vars.
 
<snip>

As others have said, it should be working okay.

Could you post a short but complete program which demonstrates the
problem?
Seehttp://pobox.com/~skeet/csharp/complete.html

Jon

Will do - after I get some sleep, thanks Jon
 
Oops... Sorry! I don't know what I was thinking. Or rather, yes, I know
what I was thinking: the Singleton pattern, where you always get the same
instance... but you get it from a function, not from the constructor. Sorry
about the hasty answer.

Oh now I've got no hope. I might have been returning the address of
the pointer but that was my only explanation for why updating any item
in the array updates all items. And I was kinda hoping I knew so
little about C# that I was just messing up my constructor. I suppose
I'll post my actual code tomorrow and see if anyone can spot my dumb
mistake. Thanks everyone. Glad to know that I'm not too far off
track and that C# isn't as esoteric as I was beginning to fear.
 
Jon said:
Nearly, at least. There's an oddity with the string class is you pass
in an empty array of chars:

using System;

class Test
{
static void Main(string[] args)
{
string x = new string(new char[]{});
string y = new string(new char[]{});
Console.WriteLine(object.ReferenceEquals(x,y));
}
}
</trivia>

But that's the only example I know of :)

Speaking of that, I did a quick test the other day that was similar to
the above, except that it used empty strings instead.

Something like:

string str1 = "test string";
string str2 = "";
string str3 = string.Empty;

str1 = str1.Remove(0, str1.Length);

The references didn't wind up equal. Particularly surprising was that
"" is not reference equal to string.Empty. I wasn't really all that
surprised that str1 didn't wind up equal to anything else, but I was
surprised that str2 and str3 weren't equal references.

Given that you do get the same results passing different empty char[]
instances, why aren't these other cases doing the same thing? Any ideas?

Pete
 
Peter said:
Jon said:
Nearly, at least. There's an oddity with the string class is you pass
in an empty array of chars:

using System;

class Test
{
static void Main(string[] args)
{
string x = new string(new char[]{});
string y = new string(new char[]{});
Console.WriteLine(object.ReferenceEquals(x,y));

I believe that these instances are the same because of this comment in
the documentation for the char[] constructor: "If value is a null
reference or contains no element, an Empty instance is initialized." So
your code is the same as doing this:

string x = string.Empty;
string y = string.Empty;
Speaking of that, I did a quick test the other day that was similar to
the above, except that it used empty strings instead.

Something like:

string str1 = "test string";
string str2 = "";
string str3 = string.Empty;

str1 = str1.Remove(0, str1.Length);

The references didn't wind up equal. Particularly surprising was that
"" is not reference equal to string.Empty. I wasn't really all that
surprised that str1 didn't wind up equal to anything else, but I was
surprised that str2 and str3 weren't equal references.

I believe that whenever you use '""' in a C# program, the compiler emits
a new instance for each of those quote pairs. Since string.Empty is
itself an instance of the string class then string.Empty does not refer
to the same instance as "".

Strings are immutable so when you did this:

str1 = str1.Remove(0, str1.Length);

The old instance of str1 was replaced with a whole new instance.

I don't remember the details off-hand, but the CLR also has special
built-in support for strings that no other object has. Or maybe it's
special IL instructions, I forget.

I have a FastString class that I found on the net somewhere years ago.
It uses unsafe code to mess with String objects "in place," must like
how you mess with strings in C/C++ by using the pointers. This code uses
string pointers in C#. I use this class very rarely and only when string
code must absolutely run faster.
Given that you do get the same results passing different empty char[]
instances, why aren't these other cases doing the same thing? Any ideas?

See above. Make sense?
 
G.Doten wrote:
[...]
Given that you do get the same results passing different empty char[]
instances, why aren't these other cases doing the same thing? Any ideas?

See above. Make sense?

You didn't add anything, at least not with respect to my understanding.
I already know what the behavior is, and I already understand all of
the details you wrote. I was wondering if anyone had any insight as to
_why_ .NET is designed this way.

They special-case the empty-array so that the resulting strings are the
string.Empty instance. Why not special-case other examples, such as
empty strings ("") and empty strings (str1.Remove(0, str1.Length))?

"" would be trivial to special-case, and would not incur any run-time
overhead (the compiler could do the mapping, and I would consider it a
particular instance of string pooling, something compilers usually do
anyway). The Remove() case would incur some negligible amount of
run-time overhead, but not enough to warrant not doing IMHO (and in the
empty-string case, would actually be faster).

I could see never bothering to map empty strings to the special
string.Empty intance. I could see always mapping empty strings to the
special string.Empty instance. I don't understand why sometimes empty
strings are mapped to string.Empty and sometimes they aren't.

Pete
 
Peter Duniho said:
Speaking of that, I did a quick test the other day that was similar to
the above, except that it used empty strings instead.

Something like:

string str1 = "test string";
string str2 = "";
string str3 = string.Empty;

str1 = str1.Remove(0, str1.Length);

The references didn't wind up equal. Particularly surprising was that
"" is not reference equal to string.Empty. I wasn't really all that
surprised that str1 didn't wind up equal to anything else, but I was
surprised that str2 and str3 weren't equal references.

Given that you do get the same results passing different empty char[]
instances, why aren't these other cases doing the same thing? Any ideas?

I think it depends on the pool of literals being used. I don't know the
exact details of how literals all work - I *think* there's one string
literal pool per AppDomain, and it's possible that there's a separate
one for domain-neutral assemblies. Or I could be talking rubbish :)
 
G.Doten said:
static void Main(string[] args)
{
string x = new string(new char[]{});
string y = new string(new char[]{});
Console.WriteLine(object.ReferenceEquals(x,y));

I believe that these instances are the same because of this comment in
the documentation for the char[] constructor: "If value is a null
reference or contains no element, an Empty instance is initialized." So
your code is the same as doing this:

string x = string.Empty;
string y = string.Empty;

Well, that just says it's documented - it doesn't explain *how* it
happens. Calling a constructor should logically (and even according to
the C# spec) create a new object. You certainly can't create a C# class
which has this behaviour, for instance.
I believe that whenever you use '""' in a C# program, the compiler emits
a new instance for each of those quote pairs. Since string.Empty is
itself an instance of the string class then string.Empty does not refer
to the same instance as "".

C# guarantees that all uses of the same string constant within an
assembly use the same object.
Strings are immutable so when you did this:

str1 = str1.Remove(0, str1.Length);

The old instance of str1 was replaced with a whole new instance.

Well, the value of str1 was replaced with a reference to a different
instance. The string itself wasn't being replaced.

However String.Remove certainly *could* detect that the result was
going to be an empty string, and return string.Empty instead of
creating a new empty string.
 
Peter said:
G.Doten wrote:
[...]
I was wondering if anyone had any insight as to
_why_ .NET is designed this way.

As I said, because strings are immutable and are treated specially by
the run-time.
 
Jon said:
G.Doten said:
static void Main(string[] args)
{
string x = new string(new char[]{});
string y = new string(new char[]{});
Console.WriteLine(object.ReferenceEquals(x,y));
I believe that these instances are the same because of this comment in
the documentation for the char[] constructor: "If value is a null
reference or contains no element, an Empty instance is initialized." So
your code is the same as doing this:

string x = string.Empty;
string y = string.Empty;

Well, that just says it's documented - it doesn't explain *how* it
happens. Calling a constructor should logically (and even according to
the C# spec) create a new object. You certainly can't create a C# class
which has this behaviour, for instance.

Sure you can. All the char[] constructor is doing is looking to see if
the passed-in char array is null or if it has 0 elements, in which case
it sets it's internal string storage to the string.Empty instance. Any
class can easily be written to do the same. Philosophically (so to
speak) why is the constructor implemented this way? I can't say for
sure, just guess that string.Empty is ever so slightly more efficient
than created a new instance of an empty string. Seems like a worthwhile
optimization to me.
Well, the value of str1 was replaced with a reference to a different
instance. The string itself wasn't being replaced.

Well, yes, str1 is being replaced by a new string instance. The expression:

str1.Remove(0, str1.Length)

creates a new string object and it is this new instance that replaces
the old instance that str1 used to point to. Strings cannot be modified
in-place due to their immutable nature (unless unsafe code is used).
 
G.Doten said:
Peter said:
G.Doten wrote:
[...]
I was wondering if anyone had any insight as to _why_ .NET is designed
this way.

As I said, because strings are immutable and are treated specially by
the run-time.

That is not an explanation.
 
G.Doten said:
Well, that just says it's documented - it doesn't explain *how* it
happens. Calling a constructor should logically (and even according to
the C# spec) create a new object. You certainly can't create a C#
class which has this behaviour, for instance.

Sure you can. All the char[] constructor is doing is looking to see if
the passed-in char array is null or if it has 0 elements, in which case
it sets it's internal string storage to the string.Empty instance. Any
class can easily be written to do the same.

Please post some sample code in which you can instantiate the same class
twice, using the "new" operator, in which the _references_ of both
instances are identical.

[...]
Well, yes, str1 is being replaced by a new string instance.

No. The reference contained by the variable str1 is being replaced by a
new reference, which is what Jon wrote. However, the original string is
not replaced. For example:

string str1, str2;

str2 = "test string";
str1 = str2;
str1 = str1.Remove(0, str1.Length);

The original string "test string" still exists. It was not replaced. A
new string instance was created by the Remove() method, and the
reference to this new instance was assigned to str1.

None of this, however, explains why the Remove() method doesn't simply
return the string.Empty instance, rather than actually create a brand
new instance that happens to have data equivalent to string.Empty.

Pete
 
Back
Top