Search code examples
c#nullablesystem.reflectionpropertyinfo

How to get declared types using reflection


I am trying to get the declared type using reflection. It's working fine for non-nullable types, but failing for nullable types.

class Product
{
    public decimal Price { get; set; }
    public decimal? OfferPrice { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        Type t = typeof(Product);
        var properties = t.GetProperties();
        PropertyInfo price_pInfo = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "Price").FirstOrDefault();
        Console.WriteLine("Member 'Price' was defined as " + price_pInfo.PropertyType);


        PropertyInfo offerprice_pinfo = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "OfferPrice").FirstOrDefault();
        Console.WriteLine("Member 'OfferPrice' was defined as " + offerprice_pinfo.PropertyType);

    }

Output:

Member 'Price' was defined as System.Decimal
Member 'OfferPrice' was defined as System.Nullable`1[System.Decimal] //I am expecting to get Nullable<System.Decimal>

Solution

  • decimal? is syntactic sugar for Nullable<decimal>, which is a generic type. The runtime represents Nullable<decimal> as "System.Nullable`1[decimal]". From the official docs:

    The name of a generic type ends with a backtick (`) followed by digits representing the number of generic type arguments. The purpose of this name mangling is to allow compilers to support generic types with the same name but with different numbers of type parameters, occurring in the same scope.

    There is no built in way to retrieve a generic type's in-code representation, you'll have to manually edit the type name. Since you're only concerned about Nullable<T>:

    public static string GetNullableTypeCodeRepresentation(string typeName)
    {
        return Regex.Replace(typeName, @"\.Nullable`1\[(.*?)\]", ".Nullable<$1>");
    }
    

    Then:

    static void Main(string[] args)
    {
        Type t = typeof(Product);
        var properties = t.GetProperties();
        PropertyInfo price_pInfo = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "Price").FirstOrDefault();
        Console.WriteLine("Member 'Price' was defined as " + price_pInfo.PropertyType);
    
        PropertyInfo offerprice_pinfo = t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "OfferPrice").FirstOrDefault();
        Console.WriteLine("Member 'OfferPrice' was defined as " + GetNullableTypeCodeRepresentation(offerprice_pinfo.PropertyType.ToString()));
    }
    

    Prints:

    Member 'Price' was defined as System.Decimal
    Member 'OfferPrice' was defined as System.Nullable<System.Decimal>