As John mentioned in his post, every Reflection user should keep this in mind: our code should not count on the member order returned by GetFields, GetMethods and other similar GetXXXs calls.

Given two fields (F1, F2) in one type, I think the compiler is free to emit F1 before F2, or vice versa. Also reflection does not guarantee to return fields in the order of how they present in the metadata; GetXXXs could be returning the members in different order at different running moments, which is illustrated by the following code. (Of course, they should have the same set of members)

using System;
using System.Reflection;

public class C {
  public int F1;
  public int F2;

  static void Main() {
    typeof(C).GetField("F2"); // Line 9

    PrintFields(); // Line 11
    PrintFields(); // Line 12

    GC.Collect();
    GC.WaitForPendingFinalizers(); // Line 15

    PrintFields(); // Line 17
    PrintFields(); // Line 18
}

  static void PrintFields() {
    foreach (FieldInfo fi in typeof(C).GetFields())
      Console.Write("{0} ", fi.Name);
    Console.WriteLine();
  }
}

Below is mostly like what you will get when running under .NET 2.0 (50727.42). You may download the attached C# file, and get it a try. (By the way, the PE file generated by C# compiler has F1 before F2 in the metadata).

> FieldOrder.exe
F1 F2
F2 F1
F1 F2
F1 F2

Behind the scenes is reflection 2.0's MemberInfo caching mechanism. Joel Pobar’s MSDN article has a nice overview about it. Here are some basic facts before diving into each important line.

  1. Each type has its own MemberInfo caches. Reflection creates different caches for different member types, one for each (if necessary): FieldInfo, MethodInfo, ConstructorInfo...
  2. The caches are created and populated lazily. If ConstructorInfos are never asked, the ConstructorInfo cache will not be created. If only one method is requested for MethodInfo, only that method’s MethodInfo will be populated into the MethodInfo cache, not with all other methods.
  3. The type keeps a weak reference to its MemberInfo caches, so, for example, when there is no reference to this type’s FieldInfos, the runtime could reclaim the memory used by the FieldInfo cache.

Line 9: we are asking for FieldInfo of "F2". At that time, the type C’s FieldInfo cache is null, Reflection will create the cache, look for the field with name "F2", create an object RuntimeFieldInfo, and put it into the cache. Imagining the cache looks like an ArrayList, the first element of the cache now contains FieldInfo "F2".

fieldInfo cache change

Line 11: typeof(C).GetFields() asks for type C's all fields (with default binding flags). It goes through all fields, and returns an array of FieldInfo (in order of F1/F2). Before returning this array back to the user, it updates the cache: appending "F1" after "F2" ("F2" already exists as the result of line 9). It also marks this cache "complete".

Line 12: the 2nd GetFields notices that the cache was built completely, immediately scans the cache, and returns an array of FieldInfo F2/F1 in that order.

Line 17: the 3rd GetFields occurs after GC, where the FieldInfo cache was reclaimed; GetFields has to re-create the cache. It goes through all fields again, and the cache is filled with FieldInfos in the order of F1/F2.

Line 18: same reason as the 2nd GetFields call, it returns F1/F2 quickly.

The picture at right shows the appearance/changes of the FieldInfo cache. Note the cache after line 11 has F2 in front of F1, GetFields at that line still returns F1/F2.