Member Order Returned by GetFields, GetMethods ...

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.

MemberOrder.cs

Comments

  • Anonymous
    July 09, 2006
    Hi,

    While I appreciate (and agree with) this point, it does remind me of the fact that sometimes you need a more accurate window into the metadata and it is unfortunate that Reflection doesn't provide that option. Here are a couple of examples:
    - For types that have LayoutKind.Sequential, the order of the fields is significant and it would be nice to have a way to return the fields in the right order.
    - For COM interop method order can be significant, but there is no way to get the methods in declaration order.
    - In my Java runtime, I would like to have the ability to query what interfaces a type implements directly (and in what order), but this is currently not possible.

    I know that there are low level (unmanaged) metadata APIs, but they are more difficult to use and don't work in conjunction with dynamically generated Ref.Emit code.

  • Anonymous
    July 09, 2006
    Haibo Luo nous livre un post intéressant dans lequel il insiste sur le fait que nous ne pouvons faire...

  • Anonymous
    July 10, 2006
    Jeroen - thanks for your comment. I will ask our feature PM to consider whether/how we can support those scenarios.

  • Anonymous
    July 24, 2006
    PingBack from http://technote.thedeveloperside.com/?p=49

  • Anonymous
    September 08, 2006
    The comment has been removed

  • Anonymous
    August 01, 2007
    The order of field definitons within classes is important metadata. We are currently using the fixed order returned from C# 1.0 to decide in what order to put some classes into a network stream.  Need a method that returns the same order as in 1.0.

  • Anonymous
    August 02, 2007
    You may try to get all FieldInfos first, and then sort by MetadataToken.

  • Anonymous
    August 03, 2007
    I just found a solution for our open source project http://www.filehelpers.com The best way that I found is to clear the cache of the RunTimeType using reflection to solve another refleciton problem :P This is some of the code where mRecordType is the Type to be cleared object cache = mRecordType.GetType().GetProperty("Cache", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic).GetValue(mRecordType, null); cache.GetType().GetField("m_fieldInfoCache", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic).SetValue(cache, null); In that way the framework always return the fields in the declaration order, I know that is not a good practice, but is better than the other solutions that I tried with GC or so. I only want to ask what permisson I need to run this code ?? ie. our code has reflection permission, we need some other ?? Best Regards Marcos Open Source developer Filehelpers Project www.filehelpers.com

  • Anonymous
    January 07, 2008
    Really simple Business Rule validation using lambdas