Search code examples
c#filehelpers

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?


Solution

  • When you write autoprops, the compiler generates backing fields for you:

    enter image description here

    FileHelpers has ReflectionHelper.cs that picks up the fields in a type you want it to parse:

    enter image description here

    This means, for my dumb class in the first screenshot, it will pick up the two __BackingField, and the two normally named fields:

    enter image description here

    When FileHelpers is processing these in FieldBase.cs, for any backing fields that have a friendly name, it goes for the property:

    enter image description here

    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:

    enter image description here

    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.