区分 getter-only 属性和表达式主体属性?

SFu*_*n28 5 .net c# reflection .net-4.0 c#-7.0

是否可以使用反射来区分 getter-only 属性和表达式主体属性?

class MyClass
{
    DateTime GetterOnly { get; }

    DateTime ExpressionBody => DateTime.Now;
}
Run Code Online (Sandbox Code Playgroud)

例如,下面的方法如何完成?

enum PropertyKind
{
    NotInteresting,

    GetterOnly,

    ExpressionBody,
}

PropertyKind GetPropertyKind(PropertyInfo propertyInfo)
{
    if (propertyInfo.GetSetMethod(true) == null)
    {
        // what goes here??
    }

    return PropertyKind.NotInteresting;
}
Run Code Online (Sandbox Code Playgroud)

相关文章: getter-only 自动属性和表达式主体属性有什么区别?

Mat*_*son 5

首先我们必须定义我们的术语:

  • 自动属性 ​​- 具有由编译器自动生成的支持字段的属性。
  • 表达式主体属性 - 使用=>(lambda) 语法实现的属性。
  • 函数体属性 - 使用正常{...}语法实现的属性。

需要注意的是,不可能区分表达式主体属性和函数主体属性,因为实际上将为两者生成相同的 IL。

但是,我相信您真正想要的是能够区分自动属性和非自动属性。

这是可能的,因为编译器生成一个[CompilerGeneratedAttribute]用属性派生的名称装饰的支持字段,可以对其进行测试。

支持字段名称当前始终为“<PropertyName>k__BackingField”(其中PropertyName是属性的名称),对于 .Net 4.6 和 .Net Core 3.1 都是如此 - 但当然这不能保证永远不会改变,因此任何依赖于此的代码都可能会在未来版本的 C# 编译器中崩溃。

尽管有相当大的警告,您可以编写一个方法来检查 a 是否PropertyInfo实现了自动属性,如下所示:

public static bool IsAutoProperty(PropertyInfo property)
{
    string backingFieldName = $"<{property.Name}>k__BackingField";
    var    backingField     = property.DeclaringType.GetField(backingFieldName, BindingFlags.NonPublic | BindingFlags.Instance);

    return backingField != null && backingField.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) != null;
}
Run Code Online (Sandbox Code Playgroud)

这将检查属性以查看 (a) 它是否具有一个具有从属性名称派生的特定名称的支持字段,以及 (b) 该支持字段是编译器生成的。

我认为这不是一个好主意,因为它依赖于未记录的和凭经验确定的编译器行为,因此需要谨慎!

这是一个可编译的控制台应用程序来演示:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{
    static class Program
    {
        static void Main(string[] args)
        {
            var type = typeof(MyClass);

            foreach (var property in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
            {
                if (IsAutoProperty(property))
                    Console.WriteLine($"{property.Name} is an auto-property");
            }
        }

        public static bool IsAutoProperty(PropertyInfo property)
        {
            string backingFieldName = $"<{property.Name}>k__BackingField";
            var    backingField     = property.DeclaringType.GetField(backingFieldName, BindingFlags.NonPublic | BindingFlags.Instance);

            return backingField != null && backingField.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) != null;
        }
    }

    class MyClass
    {
        DateTime GetterOnly { get; }

        DateTime ExpressionBody => DateTime.Now;
    }
}                                                                                                 
Run Code Online (Sandbox Code Playgroud)

这输出:

GetterOnly 是一个自动属性