Is it possible, using reflection, to distinguish between a getter-only property and an expression body property?
class MyClass
{
    DateTime GetterOnly { get; }
    DateTime ExpressionBody => DateTime.Now;
}
For example, how could the method below be completed?
enum PropertyKind
{
    NotInteresting,
    GetterOnly,
    ExpressionBody,
}
PropertyKind GetPropertyKind(PropertyInfo propertyInfo)
{
    if (propertyInfo.GetSetMethod(true) == null)
    {
        // what goes here??
    }
    return PropertyKind.NotInteresting;
}
Related post: What is the difference between getter-only auto properties and expression body properties?
First we must define our terms:
=> (lambda) syntax.{...} syntax.It is important to note that it is not possible to differentiate between an expression-bodied property and a function-bodied property, because effectively the same IL will be generated for both.
However, I believe that what you actually want is to be able to tell the difference between an auto-property and a non-auto-property.
This is possible because the compiler generates a backing field decorated with [CompilerGeneratedAttribute] and with a name derived from the property, which can be tested for.
The backing field name is currently always "<PropertyName>k__BackingField" (where PropertyName is the name of the property), and this is true for both .Net 4.6 and .Net Core 3.1 - but of course this is in no way guaranteed to never change, so any code that relies on this is likely to break for future versions of the C# compiler.
Notwithstanding that rather large caveat, you can write a method to check if a PropertyInfo implements an auto-property like so:
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;
}
This inspects the property to see if (a) it has a backing field with a specific name derived from the property name and (b) that backing field is compiler generated.
I don't think this is a good idea, because it relies on undocumented and empirically-determined compiler behaviour, so caution is required!
Here's a compilable console app to demonstrate:
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;
    }
}                                                                                                 
This outputs:
GetterOnly is an auto-property
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With