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.