Getting fields by reflection asynchronously

2019-07-03 17:03发布

问题:

I am trying to get public fields using reflection from assembly.

Assembly contain just one class as below:

public abstract class User
{
        private object _iAmPrivateField;
        protected object IAmProtectedField;
        internal object IAmInternalField;
        public object IAmPublicField;

        private void IAmPrivateMethod() { }
        protected void IAmProtectedMethod() { }
        internal void IAmInternalMethod() { }
        public void IAmPublicMethod() { }

        private object IAmPrivateProperty { get; set; }
        protected object IAmProtectedProperty { get; set; }
        internal object IAmInternalProperty { get; set; }
        public object IAmPublicProperty { get; set; }              
}

This is the method which retrieves public fields from given assembly:

public FieldInfo[] GetPublic(Assembly assembly)
{
     return assembly.GetTypes()
                    .SelectMany(x => x.GetFields(BindingFlags.Public | BindingFlags.Instance))
                    .Where(x => x.IsPublic).ToArray();
}

The above example is working as expected - the result is 1.

However I added async method inside the class

public async Task IAmAsyncMethod() { }

After above change, GetPublic() returns 4 instead of 1.

Is there an option to filter these fields that GetFields() still returns 1?

回答1:

async works by emitting an extra class containing information about the async state machine. You can see it if you inspect the assembly using a disassembler such as ILSpy. For more information on this concept, see https://www.markopapic.com/csharp-under-the-hood-async-await/ or Google "c# async state machine". State machines are also generated for methods that use yield return ...

This is what your auto-generated async state machine looks like, as decompiled by ILSpy:

[CompilerGenerated]
[Serializable]
private sealed class <>c
{
    public static readonly Program.<>c <>9 = new Program.<>c();

    public static Func<Type, IEnumerable<FieldInfo>> <>9__0_0;

    public static Func<FieldInfo, bool> <>9__0_1;

    internal IEnumerable<FieldInfo> <Main>b__0_0(Type x)
    {
        return x.GetFields(BindingFlags.Instance | BindingFlags.Public);
    }

    internal bool <Main>b__0_1(FieldInfo x)
    {
        return x.IsPublic;
    }
}

You can omit special auto-generated classes like that by checking for the CompilerGeneratedAttribute. Amend your code as:

return assembly.GetTypes()
    .Where(t => t.GetCustomAttribute<CompilerGeneratedAttribute>() == null)
    .SelectMany(x => x.GetFields(BindingFlags.Public | BindingFlags.Instance))
    .Where(x => x.IsPublic)
    .ToArray();


标签: c# reflection