ArrayList vs. List<>

P

Peter Duniho

Which brings me to another question: how is passing a reference by value
not pass-by-reference?

In some sense it is. Consider, for example, the C way of doing things in
which everything is really passed by value, but you can effectively pass
something by reference by explicitly passing a reference to it. But of
course when you did that, you had to explicitly dereference the passed in
reference value. Even though C always passes things by value, you could
still essentially pass things by reference by doing it explicitly:

void SomeFunction(int *pi)
{
// The parameter is "pi"...to modify the original, you need to
// explicitly dereference "pi" using the "*" operator
*pi = 5;
}

void OtherFunction()
{
int i = 4;

SomeFunction(&i);
printf("%d\r\n", i);
}

Output:
5

Later on, we got true "passing by reference" in C++, where you simply used
the parameter identifier, but it was a reference to the original value
from the caller. This is like using the "Var" keyword in Pascal, for
example. And, of course, like using the "ref" or "out" keywords in C#.
For example:

void SomeFunction(int &i)
{
// The parameter is "i"...no extra work is necessary to modify
// the original.
i = 5;
}

void OtherFunction()
{
int i = 4;

SomeFunction(&i);
printf("%d\r\n", i);
}

Output:
5

IMHO, one very nice thing about distinguishing between passing a reference
and passing BY reference is that it makes it simpler when you are passing
a reference to a reference. In C, you have to do this by passing a
pointer to a pointer, which makes some people's head hurt. Granted, maybe
passing references by reference in C# makes some peoples head hurt too,
but I think maybe it makes fewer people's head hurt. :)

All that said, note that I only said "in some sense". I don't believe
it's true in the most useful sense...just in a certain way of looking at
it. In the most useful sense, passing a reference by value is definitely
*not* the same as passing by reference. That is, when you pass a
reference by value, the object to which the reference refers can be
directly modified. But the original reference that you passed in is *not*
changeable. A copy of that reference was made, and if the called function
changes the reference itself, that change will be reflected only within
the called function.

Only by passing a reference by reference can the called function modify
the original reference. And that's the difference between passing a
reference by value and passing it by reference.

For what it's worth, I still think Jon's write-up is a great place to
figure this stuff out, but since I went to the trouble of writing my own
little summary in a previous post, I am copying it here in case you find
it useful:

---- begin quoted text ----
By default passing is always "by value". The "ref" and "out" keywords
cause things to be passed "by reference". Not to be confused with the
difference between "value types" and "reference types". Again, I think
Jon's write-up does a good job of outlining the various combinations of
value and reference types being passed by value and by reference. A quick
summary though:

1) pass value type by value: a copy of the value type is made and
passed to the method
example:
void method1(int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(y);
Console.WriteLine(y);
}

output:
4

2) pass reference type by value: a copy of the reference is made and
passed to the method, but it's important to note that it's the *reference*
being copied, not the object itself. So unless the reference type is
immutable (like String, for example), the object itself can be modified by
the called method (but the reference to the object cannot be)
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(y);
Console.WriteLine(y.i);
}

output:
5

3) pass value type by reference: a reference to the value type is
passed, and the called method can modify the original value type instance
example:
void method1(ref int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(ref y);
Console.WriteLine(y);
}

output:
5

4) pass reference type by reference: a reference to the reference is
made; not only can the reference type object be modifed as in case #2, but
the reference *to* that object can be modified as well.
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(ref A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(ref y);
Console.WriteLine(y.i);
}

output:
6
---- end quoted text ----

Pete
 
F

Frans Bouma [C# MVP]

Jon said:
So C# programmers don't regard reference types as boxed. I didn't
know that.

Frans' statement seems to have been about referential transparency
then.

What I wanted to make clear is that a string isn't a value type,
although it acts like one. Often, people make the mistake that they
think a string is a value type and as value types are 'boxed' when
added to an object typed data structure like ArrayList, they assume
strings are boxed as well and thus run into a performance hit. This
isnt' the case, strings aren't value types and therefore there's no
boxing taking place, as 'boxing' in this context is the objectification
of a value type which doesn't happen so no performance loss.

That was the cause of my reply :).

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
F

Frans Bouma [C# MVP]

Jon said:
Can you elaborate on this? Presumably they end up getting passed as a
pointer to a char array?

see my reply deeper in the thread and Jon's explanation.
I just timed this and the generic Dictionary was much faster than the
non-generic Hashtable for double->double mappings (timings are best
of 3):

Insert:
Hashtable 31.0s
Dictionary 4.90s

Fetch:
Hashtable 6.09s
Dictionary 3.20s

Can you cite some benchmark results where Hashtable is faster?

With objects I see different results:
Iteration: 0
1000000 inserts into Hashtable took: 1587ms
1000000 inserts into dictionary took: 1256ms
Iteration: 1
1000000 inserts into Hashtable took: 1737ms
1000000 inserts into dictionary took: 1248ms
Iteration: 2
1000000 inserts into Hashtable took: 1612ms
1000000 inserts into dictionary took: 1844ms

code:
public class A
{
private string _foo;

public string Foo
{
get
{
return _foo;
}
set
{
_foo = value;
}
}

}

public class Program
{
public static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();

int amount = 1000000;

for(int l = 0; l < 3; l++)
{
sw.Reset();
Console.WriteLine("Iteration: {0}", l);

Hashtable ht = new Hashtable(amount);
Dictionary<string, A> dt= new Dictionary<string, A>(amount);
sw.Start();
for(int i = 0; i < amount; i++)
{
string name = i.ToString();
A a = new A();
a.Foo = name;
ht.Add(name, a);
}

sw.Stop();
Console.WriteLine("{0} inserts into Hashtable took: {1}ms", amount,
sw.ElapsedMilliseconds);

sw.Reset();
sw.Start();
for(int i = 0; i < amount; i++)
{
string name = i.ToString();
A a = new A();
a.Foo = name;
dt.Add(name, a);
}

sw.Stop();
Console.WriteLine("{0} inserts into dictionary took: {1}ms", amount,
sw.ElapsedMilliseconds);
}
}
}

Though this isn't scientific. What I've seen in our framework is that
where we store fieldname-index tuples into a dictionary, it is in
profiling slower than with a hashtable. WHich I found odd of course,
but I couldn't explain why it was. I probably should have said: "some"
situations instead of 'many'.

One serious bottleneck in the dictionary is binary serialization. It's
so incredibly slow and gives more data than the hashtable it's not even
funny. For that I've written a 'FastDictionary':

/// <summary>
/// Utility class which can be used instead of a normal Dictionary
class when the Dictionary class is serialized. This class is faster and
/// has much less overhead than the normal dictionary class, as it
doesn't use generic types.
/// </summary>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TValue">Value type</typeparam>
[Serializable]
public class FastDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
#region Class Member Declarations
[NonSerialized]
private SerializationInfo _info;
#endregion

/// <summary>
/// CTor
/// </summary>
/// <param name="capacity"></param>
public FastDictionary( int capacity )
: base( capacity )
{
}

/// <summary>
/// CTor
/// </summary>
public FastDictionary() : base()
{
}

/// <summary>
/// CTor
/// </summary>
/// <param name="d"></param>
public FastDictionary(IDictionary<TKey, TValue> d)
: base( d )
{
}

/// <summary>
/// Deserialization CTor
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
protected FastDictionary(SerializationInfo info, StreamingContext
context)
{
_info = info;
}

/// <summary>
/// Implements the <see
cref="T:System.Runtime.Serialization.ISerializable"></see> interface
and returns the data needed to serialize the <see
cref="T:System.Collections.Generic.Dictionary`2"></see> instance.
/// </summary>
/// <param name="info">A <see
cref="T:System.Runtime.Serialization.SerializationInfo"></see> object
that contains the information required to serialize the <see
cref="T:System.Collections.Generic.Dictionary`2"></see>
instance.</param>
/// <param name="context">A <see
cref="T:System.Runtime.Serialization.StreamingContext"></see> structure
that contains the source and destination of the serialized stream
associated with the <see
cref="T:System.Collections.Generic.Dictionary`2"></see>
instance.</param>
/// <exception cref="T:System.ArgumentNullException">info is
null.</exception>
public override void
GetObjectData(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
{
object[] keys = new object[base.Count];
object[] values = new object[base.Count];

int i = 0;
foreach(KeyValuePair<TKey, TValue> pair in this)
{
keys = pair.Key;
values = pair.Value;
i++;
}

info.AddValue("keys", keys);
info.AddValue("values", values);
}

/// <summary>
/// Implements the <see
cref="T:System.Runtime.Serialization.ISerializable"></see> interface
and raises the deserialization event when the deserialization is
complete.
/// </summary>
/// <param name="sender">The source of the deserialization
event.</param>
/// <exception
cref="T:System.Runtime.Serialization.SerializationException">The <see
cref="T:System.Runtime.Serialization.SerializationInfo"></see> object
associated with the current <see
cref="T:System.Collections.Generic.Dictionary`2"></see> instance is
invalid.</exception>
public override void OnDeserialization(object sender)
{
object[] keys = (object[])_info.GetValue("keys", typeof(object[]));
object[] values = (object[])_info.GetValue("values",
typeof(object[]));

for(int i = 0; i < keys.Length; i++)
{
base.Add((TKey)keys, (TValue)values);
}
}
}

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
A

Alvin Bruney [MVP]

In some sense it is. Consider, for example, the C way of doing things in
"C"? Is that still around? Yuck.

--
Regards,
Alvin Bruney
------------------------------------------------------
Shameless author plug
Excel Services for .NET is coming...
https://www.microsoft.com/MSPress/books/10933.aspx
OWC Black Book www.lulu.com/owc
Professional VSTO 2005 - Wrox/Wiley


Which brings me to another question: how is passing a reference by value
not pass-by-reference?

In some sense it is. Consider, for example, the C way of doing things in
which everything is really passed by value, but you can effectively pass
something by reference by explicitly passing a reference to it. But of
course when you did that, you had to explicitly dereference the passed in
reference value. Even though C always passes things by value, you could
still essentially pass things by reference by doing it explicitly:

void SomeFunction(int *pi)
{
// The parameter is "pi"...to modify the original, you need to
// explicitly dereference "pi" using the "*" operator
*pi = 5;
}

void OtherFunction()
{
int i = 4;

SomeFunction(&i);
printf("%d\r\n", i);
}

Output:
5

Later on, we got true "passing by reference" in C++, where you simply used
the parameter identifier, but it was a reference to the original value
from the caller. This is like using the "Var" keyword in Pascal, for
example. And, of course, like using the "ref" or "out" keywords in C#.
For example:

void SomeFunction(int &i)
{
// The parameter is "i"...no extra work is necessary to modify
// the original.
i = 5;
}

void OtherFunction()
{
int i = 4;

SomeFunction(&i);
printf("%d\r\n", i);
}

Output:
5

IMHO, one very nice thing about distinguishing between passing a reference
and passing BY reference is that it makes it simpler when you are passing
a reference to a reference. In C, you have to do this by passing a
pointer to a pointer, which makes some people's head hurt. Granted, maybe
passing references by reference in C# makes some peoples head hurt too,
but I think maybe it makes fewer people's head hurt. :)

All that said, note that I only said "in some sense". I don't believe
it's true in the most useful sense...just in a certain way of looking at
it. In the most useful sense, passing a reference by value is definitely
*not* the same as passing by reference. That is, when you pass a
reference by value, the object to which the reference refers can be
directly modified. But the original reference that you passed in is *not*
changeable. A copy of that reference was made, and if the called function
changes the reference itself, that change will be reflected only within
the called function.

Only by passing a reference by reference can the called function modify
the original reference. And that's the difference between passing a
reference by value and passing it by reference.

For what it's worth, I still think Jon's write-up is a great place to
figure this stuff out, but since I went to the trouble of writing my own
little summary in a previous post, I am copying it here in case you find
it useful:

---- begin quoted text ----
By default passing is always "by value". The "ref" and "out" keywords
cause things to be passed "by reference". Not to be confused with the
difference between "value types" and "reference types". Again, I think
Jon's write-up does a good job of outlining the various combinations of
value and reference types being passed by value and by reference. A quick
summary though:

1) pass value type by value: a copy of the value type is made and
passed to the method
example:
void method1(int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(y);
Console.WriteLine(y);
}

output:
4

2) pass reference type by value: a copy of the reference is made and
passed to the method, but it's important to note that it's the *reference*
being copied, not the object itself. So unless the reference type is
immutable (like String, for example), the object itself can be modified by
the called method (but the reference to the object cannot be)
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(y);
Console.WriteLine(y.i);
}

output:
5

3) pass value type by reference: a reference to the value type is
passed, and the called method can modify the original value type instance
example:
void method1(ref int x)
{
x = 5;
}
void method2()
{
int y = 4;

method1(ref y);
Console.WriteLine(y);
}

output:
5

4) pass reference type by reference: a reference to the reference is
made; not only can the reference type object be modifed as in case #2, but
the reference *to* that object can be modified as well.
example:
class A
{
public int i;

public A(int j)
{
i = j;
}
}
void method1(ref A x)
{
x.i = 5;
x = new A(6);
}
void method2()
{
A y = new A(4);

method1(ref y);
Console.WriteLine(y.i);
}

output:
6
---- end quoted text ----

Pete
 

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