通过反射异步获取字段

时间:2018-12-20 17:59:28

标签: c# reflection

我正在尝试使用程序集的反射来获取公共字段。

程序集仅包含一个类,如下所示:

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; }              
}

这是从给定程序集中检索公共字段的方法:

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

上面的示例按预期方式工作-结果为1。

但是我在类中添加了异步方法

public async Task IAmAsyncMethod() { }

上述更改后,GetPublic()返回4而不是1。

是否可以过滤GetFields()仍返回1的这些字段?

1 个答案:

答案 0 :(得分:7)

async通过发出包含有关异步状态机信息的额外类来工作。如果使用反汇编程序(例如ILSpy)检查装配,则可以看到它。有关此概念的更多信息,请参见https://www.markopapic.com/csharp-under-the-hood-async-await/或Google“ c#异步状态机”。还为使用yield return ...

的方法生成状态机。

这是自动生成的异步状态机的样子,由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;
    }
}

您可以通过检查CompilerGeneratedAttribute来省略特殊的自动生成类。将您的代码修改为:

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