Get property default value as expression using reflection - c#

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:
The type in attribute DefaultValue could be different from the type of the property.. No type check :(
If I only have the DefaultValue, when I write new Person(), the default values are not actually set from the attribute.
Therefore, I need to write the default value after { get; set; }, but:
Or I wrote both attribute and default value, but then I should manually mantain synchronized them.
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

Related

Any idea why private property and variable behave differently in FileHelpers?

To simplify the case, let say I have the following class
public class TestFileHelpersClass
{
[FieldOrder(1), FieldFixedLength(20), FieldTrim(TrimMode.Both)]
public string Field1 { get; set; }
[FieldOrder(2), FieldFixedLength(20), FieldTrim(TrimMode.Both)]
private string Field2 { get; set; }
// If change to a normal variable, there is no exception and FileHelper can work perfectly.
// private string Field2;
public TestFileHelpersClass()
{
this.Field1 = "Field1 Value";
this.Field2 = "Field2 Value";
}
}
Then, there is an exception thrown when I instantiate the FileHelperEngine.
static void TestFileHelpers()
{
// FileHelpers.BadUsageException: 'The field: '<Field2>k__BackingField' must be marked with the FieldFixedLength attribute because the record class is marked with FixedLengthRecord.'
FileHelperEngine<TestFileHelpersClass> engine = new FileHelperEngine<TestFileHelpersClass>();
TestFileHelpersClass a = new TestFileHelpersClass();
string result = engine.WriteString(new List<TestFileHelpersClass> { a });
}
But {Field2} has already been marked with {FieldFixedLength(20)}
If I change {Field2} from a property to variable, it is working fine.
The question is:
When it is public, FileHelpers works perfectly in both the variable and property case.
When it is private, variable is still ok, but property fails to work.
Any idea why private property and variable behave differently in FileHelpers?
When you write autoprops, the compiler generates backing fields for you:
FileHelpers has ReflectionHelper.cs that picks up the fields in a type you want it to parse:
This means, for my dumb class in the first screenshot, it will pick up the two __BackingField, and the two normally named fields:
When FileHelpers is processing these in FieldBase.cs, for any backing fields that have a friendly name, it goes for the property:
fi is the __BackingField; it does have a friendly name because it's come from PrivateProp. To go for the property, FH calls this:
var prop = fi.DeclaringType.GetProperty(fieldFriendlyName);
This call won't discover a private property because it doesn't specify any BindingFlags.NonPublic|BindingFlags.Instance, so prop ends up null and FH doesn't switch to looking at the property
Look back at the first image; the FileHelper custom attributes are on the property, not the backing field. Later in this same FieldBase.cs class, FH tries to find derivates of its own attributes (FH's attributes derive from FieldAttribute) on the member being inspected:
The member being inspected is the __BackingField, which doesn't have any FH attributes; it's the prop that has the attributes. The field does have attributes (CompilerGenerated, DebuggerBrowsable) but because this call looks only for FH's attributes and the backing field doesn't have any FH custom attributes it is essentially looking on the wrong thing. It reaches a situation where it cannot work - it needs attributes you've put on the prop, but it looks on the field, finds no FH attribs and throws an error because it needs those attributes it can never find.
If fi.DeclaringType.GetProperty(fieldFriendlyName) had instead used an overload that looked for private props e.g. fi.DeclaringType.GetProperty(fieldFriendlyName, BindingFlags.NonPublic|BindingFlags.Instance|..other_binding_flags_here) then it would have found the private prop you decorated (but I don't guarantee that this is a bug or that that is the resolution)
You could report a bug and see if the author agrees, you could amend the library yourself and use the amended version, or you could just use fields/public props instead of private props
I cannot answer why the library was coded this way; few people here can. Questions of the ilk "what was developer X thinking when..." are seldom a good fit for SO.

How to assign ByteSerialize attribute/length as a variable

I'm creating models with different types and length which are used as a request/response to/from an API.
[ByteSerialize(1)]
public string ByteLength { get; set; }
[ByteSerialize()]
public string Byte { get; set; }
I would like first to get the ByteLength that comes from the API and them to pass it to the [ByteSerialize(?)]
Attributes are inert - there's no magic way of getting things to happen automagically. Additionally, attributes are usually considered only as metadata in the IL, and as such: the parameters for them must be expressed as constants, not as runtime values. Obviously this changes if you only ever create the attributes as objects, not as metadata, but: your example shows metadata.
To do what you want, you could do it manually, i.e. have some method on ByteSerializeAttribute, and when your code detects (via PropertyInfo) that the attribute is there, use something like:
var value = property.GetValue(obj);
attrib.SomeMethod(obj, value); // or whatever you need

Set DisplayPropertyName value

I would like to know if there is a maneer (may be with data annotation) to set the value of
property.DisplayPropertyName
.
Do you have any idea please?!!
Actually I think you mean to ask how you can choose a different property as the display property (i.e., the property that is used to briefly denote objects of your class in the UI).
The Entity Framework Service that generates the metadata for scaffolding choose the first property of string type (as far as I know), but you can change that by applying the DisplayColumn attribute to your class:
[DisplayColumn("Alias")]
public class Person {
public string Name { get; set; }
public string Alias { get; set; }
}
Note, however, that this only works for properties that are actually mapped to a column in the database. (I was trying to use a computed property, not mapped, but that led to an error message while scaffolding.)
That is quite easy to do:
[DisplayName("RenamedProperty")]
you can even add a description
[Description("Demonstrates DisplayNameAttribute.")]
or perhaps you would like it to reference your resources so it adapts itself to the selected language:
[Display(Name = "Description", ResourceType = typeof(Resources))]

Is there a better way to set lots of required properties than sending as parameters in a Constructor?

I have a class that in order to do it's job needs to have 8 different property values set.
I want to make sure that all 8 properties are set before trying to execute a method.
I currently have all the parameters passed in and set via the constructor.
Is there a better way to do this?
You can allow the object to be created without specifying values for all the properties and then the method would throw an InvalidOperationException if called before the object state is valid for the method call to execute, which in this case would mean that all 8 properties would have valid values.
This way you give more flexibility to the consumer. It can create a new instance at one moment, set it's properties at another and only then call the method. This is a "pattern" that is used through the .NET codebase and to which developers are already used.
Update:
This also simplifies things if you're adding other methods that don't need the full set of properties to be initialized. Yes we could add another constructor overload with the new set of properties, but what if we have 2 methods that both need one property of the same type to be initialized? This is not solvable by using constructor overloads.
In my opinion if a class requires these 8 objects in order to function then they should be passed into the constructor and by no other means. I'm a big fan of dependency injection anyway, but this method also allows for better unit testing by passing in mocked objects and such.
you could consolidate the parameters into a Parameter Object and just pass that instead.
If you were using this class from XAML you would make all 8 properties individually settable and throw an exception if somebody tries to execute a method without them all set. It's also possible that you would want to "freeze" the object once a method was called and throw an exception if somebody tries to set a property on a "frozen" object.
I'm not going to pass judgement on which method is better, though.
A constructor is the only way to do it at compile time. The other option would be to throw an exception if not all the parameters have been set.
Give them default values?
Maybe you can give a bit more of the context to let us help you!
Use setter in the properties. When property is used by the method and it is not set already, setter would do it for you. This is not the recommended approach thogh but might be needed in some situations.
Some thing similar would work
Class SomeClass
{
string name;
string Name
{
set
{
if (name == null)
name = value;
}
}
}
The constructor sounds like the best way to do this since it will be verified at compile tile. Of course, you could have default values for some or all of the properties and then chain constructors, so that not every property needs to be set in the constructor. Something like:
public class Person
{
public string Name { get; set; }
public double Height { get; set; }
public DateTime DoB { get; set; }
public Person(string name, double height, DateTime dob) : this(name, height)
{
this.DoB = dob;
}
public Person(string name, double height)
{
this.Name = name;
this.Height = height;
this.DoB = DateTime.Now.Date;
}
}
This means you can construct a new Person object using either two or three parameters, but all will be set (but if you use two then DOB will get a default of today):
Person person1 = new Person("Geoff", 1.8, new DateTime(1950, 5, 12));
Person person2 = new Person("John", 1.54); // Gets default DOB
You can use an object initializer if you are using c# 3.0 onwards. This is arbitrary whether it's 'better' or not.

How to make a Attribute aware of the Name of the Proprty it is on?

I want to implement a simple attribute that is used to map Database Columns to Properties.
So what i have so far is something that attached like so:
[DataField("ID")]
public int ID { get; set; }
[DataField("Name")]
public String Name { get; set; }
[DataField("BirD8")]
public DateTime BirthDay { get; set; }
Is there a way that I can make the attribute "aware" of the field it is on, so that for the properties where the name is the same as the ColumnName I can just apply the attribute without the name parameter, or would I have to deal with that at the point where I reflect the properties. I want to end up doing just this:
[DataField]
public int ID { get; set; }
[DataField]
public String Name { get; set; }
[DataField("BirD8")]
public DateTime BirthDay { get; set; }
The attribute itself won't be aware of what it's applied to, but the code processing the attributes is likely to be running through PropertyInfo values etc and finding the attributes associated with them. That code can then use both the property and the attribute appropriately.
To make things simpler, you might want to write a method on the attribute to allow it to merge its information with the information from the property, so you'd call:
DataFieldAttribute dfa = propertyInfo.GetCustomAttributes(...); // As normal
dfa = dfa.MergeWith(propertyInfo);
Note that for the sake of sanity this should create a new instance of the attribute, rather than changing the existing one. Alternatively, you might want a whole separate class to represent "the information about a data field":
DataFieldAttribute dfa = propertyInfo.GetCustomAttributes(...); // As normal
DataFieldInfo info = dfa.MergeWith(propertyInfo);
That way you could also construct DataFieldInfo objects without any reference to attributes, which might be a nice conceptual separation - allowing you to easily load the config from an XML file or something similar if you wanted to.
If you don't mind using postsharp you can look Here, at a previous question I have asked which was close. I ended up using the compile time validate to do what I wanted, although there are other options, like CompileTimeInitalize.
public override void CompileTimeInitialize(object element)
{
PropertyInfo info = element as PropertyInfo;
//....
}

Categories