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