mercredi 8 juin 2022

Get property default value as expression using reflection

In order to build a custom transpiler, I'm trying to get the default value of all the properties inside a class as an expression, and not as a value itself.

Let me bring you some examples to clarify what I'm trying to do and what I've done/tried/investigated so far.


Source code could be the following one:

const string DATETIME_NOW = "____DATETIME_NOW____";

public class Person {
  [DefaultValue("Foo")]
  public string Name { get; set; } = "Foo";

  [DefaultValue(DateTime.Now)] // This is not doable: "An attribute argument must be a constant expression"
  public DateTime DateOfBirth { get; set; } = DateTime.Now;

  [DefaultValue(DATETIME_NOW)]
  public string DateOfBirthStringed { get; set; } = DATETIME_NOW; // which acts like DateTime.Now.ToString()
}


The ultimate goal of the transpiler, is to obtain a Javascript class that looks like this:

class Person {
  name: string = "Foo";
  dateOfBirth: Date = new Date(Date.now());
  dateOfBirthStringed : Date = Date.now();
}

My current, and working, implementation is the use of DefaultValue attribute with some constants strings used when the default value is an expression (e.g. DateOfBirthStringed). What I'm doing is using reflection on Person, getting all the PropertyInfo, looking for their DefaultValue attribute, and then checking if the given default value are some fixed constants like DATETIME_NOW.

This works, but I've a couple of problems:

  1. The type in attribute DefaultValue could be different from the type of the property.. No type check :(
  2. If I only have the DefaultValue, when I write new Person(), the default values are not actually set from the attribute.
  3. Therefore, I need to write the default value after { get; set; }, but:
    1. Or I wrote both attribute and default value, but then I should manually mantain synchronized them.
    2. I write only the default value, but then I've no way to get it with reflection.

About point 3.2, why I can't get the default value via reflection? Suppose the Person class defined above; if I use reflection, I need to instantiate it, but once instantiated, Person.DateOfBirth has an actual DateTime value, and I cannot know it was coming from DateTime.Now.

Also, if Person would be abstract.. Well, no way to instantiate it.


So, basically, the only way I could perfectly transpile the code is to read the code as a tree, something like parseTreeCode(typeof(Person)). At that point, I should navigate the tree and be able to do everything I want.

I did find Roslyn, which allows me to parse C# code, but.. It parses "stringed" code, and not code that belongs to the same project. I thought "well, get the file where Person is defined, and parse the whole file", but unfortunately, once the program is running, I cannot get the file where Person is defined.. I mean, I can do typeof(Person).Assembly, and getting the Assembly.. But it would be an assembly, so it would not be good.


At this point, I'm thinking that there is no real solution for this, but maybe I'm missing some C# packages/features that could help me





Aucun commentaire:

Enregistrer un commentaire