-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Constructor mapping #751
Constructor mapping #751
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. If we could add test cases init setter properties and record classes that'd be great, though we can do that in a separate change if you would prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
Constructor Mapping
This PR introduces constructor mapping to the existing default mapper. Previously, mapping was achieved by creating a new instance of the class being mapped to using its default (parameterless) constructor, and then setting the values of its properties. This is not always possible, however, as some classes only have a constructor that requires parameters. Additionally, this meant that all the mapping code had a
where T : new()
constraint, which limited the usefulness of the mapper.With constructor mapping, the default mapper will now decide which constructor to use to create the class according to the following priorities:
[MappingConstructor]
attribute.The mapper does not take into account whether it can satisfy the parameters of the constructor, since each time a record is mapped it may have a different set of fields. Therefore if you want the mapper to use a specific constructor it is recommended that you mark it with the
[MappingConstructor]
attribute.Once it has decided which constructor to use, it will map the parameters in the same way as it maps properties. This means that the parameters can be marked with a
[MappingSource]
attribute to specify the source of the value. The[MappingIgnore]
attributer is not permitted on constructor parameters since a constructor must have a value for each parameter.After the object has been constructed, any properties that are not parameters of the constructor will be mapped in the same way as before.
Examples
For all these examples, assume the following record is being mapped.
Mapping to a class with a parameterless constructor
This is the existing case. The object is created and then the propetries are set individually from values in the record. The
[MappingSource]
attribute is used to specify the source of the value; for theName
property; obviously, theAge
property is set from the value in the record with the same name.Mapping to a class with a constructor
In this case, the non-default constructor is called, and the parameters are mapped from the record. The
Name
parameter is mapped from the value in the record with the nameforename
, and theAge
parameter is mapped from the value in the record with the nameage
.Mapping to a class with a constructor marked with
[MappingConstructor]
In this case, although there is a default constructor, the constructor marked with
[MappingConstructor]
is used. TheName
parameter is mapped from the value in the record with the nameforename
. After construction, theAge
property is set from the value in the record with the nameage
.Complex Types
Parameters to a constructor are treated exactly the same as properties, so they can have nested objects, lists, etc. Both constructor and property mapping will be used as appropriate, so if you are using constructor mapping for the top-level object, there is no need to use it for nested objects. Both constructor parameters and properties may be marked with the
[MappingSource]
attribute and this will be correctly observed.