[Mono-list] Newbie and ArrayList

Jonathan Gilbert 2a5gjx302 at sneakemail.com
Mon Apr 16 01:40:32 EDT 2007


At 10:13 PM 4/15/2007 +0200, Johannes Graumann wrote:
>On Sunday 15 April 2007 21:54, Alan McGovern wrote:
>> Could you post up the entire test.cs file?
>Sure. But beware, this is very much a work in progress and I am a bloody 
>newbie ...
>
[snip]
>  [TestFixture()]
>  public class Test
>  {
>    #region Establishment of Testing Infrastructure
[snip]
>    // Construct the names needed for the mock directory structure
>    ArrayList DSYears = new ArrayList();
>    DSYears.Add(1);
[snip]

Just to give a bit more detail, basically (and I hope it's not too much
detail -- I'm a compulsive over-explainer), it looks like what you're
aiming for is to have the "DSYears" ArrayList start off with one value in
it as soon as the instance is created (i.e., at the same time that
"DSYears" is assigned its "new ArrayList()" value). Unfortunately,
*statements* must be inside the bodies of methods or accessors (properties
and events). The initializers within a class may consist only of
expressions that can be evaluated before the instance of the class has been
initialized.

A function call can be a statement when it sits alone with a semicolon at
the end of it -- as you've done above -- but at its root, it is an
expression. The "new ArrayList()" which appears in the initializer for the
DSYears field is also an expression. Because it appears in the initializer
for a variable rather than on its own, as a statement, it is permitted
outside of a method.

Based on this, you could always do something like this (but I'll show a
better way next):

  class Test
  {
    private static ArrayList CreateInitialList()
    {
      ArrayList ret = new ArrayList();

      ret.Add(1);
      return ret;
    }

    ArrayList DSYears = CreateInitialList();
  }

This would work because the call to CreateInitialList is being used as an
expression rather than a statement, and that expression is the initializer
for the DSYears field. It would be permitted even though the instance
hasn't been fully initialized yet because the CreateInitialList method is
marked "static" and thus doesn't operate on an instance of the class.

Though this works fine, there is actually a better place to put this sort
of initialization: the constructor. When you don't specify a constructor
(as is the case here), the C# compiler automatically inserts a blank one
(you can see it if you decompile the resulting EXE). All of the field
initializers are also, behind the scenes, turned into statements inside the
constructor. So, if you leave out the "DSYears.Add(1)" line, the C#
compiler automatically generates this constructor for you:

  class Test
  {
    public Test()
    {
      DSYears = new ArrayList();
    }

    ArrayList DSYears;

    ...
  }

This is because in .NET, fields don't really have initializers; that's a C#
feature. In order to translate the C# code into .NET bytecode (MSIL), it
must translate the C# idiom into the equivalent (and often less elegant)
MSIL idiom, which is to simply perform that initialization in the constructor.

So what does this mean for your code? Simply move the initialization into a
constructor explicitly, and then you can put other statements next to it.
The constructor is a kind of method, so you can use almost any statement
there that you could use elsewhere, and it is guaranteed to run for every
instance of the class. In fact, if the constructor throws an exception, the
.NET framework won't permit the resulting half-initialized object to be
used; it is discarded and the caller gets the exception instead of a
reference to a new instance of the class.

Putting the initialization into the constructor looks identical to the code
above, which showed how the C# compiler interprets your field initializer.
Instead of using the initializer, you put the initial DSYears assignment
into the constructor. Then, once you've given the field an instance of
ArrayList, you can call .Add on it to put whatever values you want into it:

  class Test
  {
    public Test()
    {
      DSYears = new ArrayList();
      DSYears.Add(1);

      // DSYears.Add(1901);

      // DSYears.AddRange(
      //   new object[]
      //   {
      //     1901,
      //     2004,
      //     711,
      //     1848,
      //     1951,
      //     1009,
      //   });
    }

    ArrayList DSYears;

    ..
  }

So there you have it :-) And if you already knew that and simply had a
brain fart, well, probably some other readers on the list will get
something out of this explanation :-)

Jonathan Gilbert


More information about the Mono-list mailing list