thomasgalliker/ObjectDumper

[Enhancement]Improved way to exclude (readonly) properties

Opened this issue · 0 comments

ibs-ts commented

Summary

Currently the only way to exclude (readonly) properties is by using DumpOptions.ExcludeProperties.
It excludes all properties matching the names in the to-dump-object(-tree).
There might be use cases, where this is not preferred.

Intended Use Case

Let say, you want to dump an object with a readonly property, my current approach to produce compilable output would be by using 'ExcludeProperties'

var point = new System.Drawing.Point() { X = 1, Y = 0 };

var str = ObjectDumper.Dump(point, new DumpOptions()
{
    IndentChar = '\t',
    IndentSize = 1,
    LineBreakChar = Environment.NewLine,
    DumpStyle = DumpStyle.CSharp,
    ExcludeProperties = new string[]
    {
        nameof(Point.IsEmpty)
    }
});
Console.WriteLine(str);

However, if you've a more complex object (tree), where property names are duplicated, this lead to 'all or nothing':

var expection = new Expection() 
{ 
    Operands = Enumerable.Range(1, 5).ToArray(),
    Result = 15
};
var tester = new Tester(expection);

var str = ObjectDumper.Dump(tester, new DumpOptions()
{
    IndentChar = '\t',
    IndentSize = 1,
    LineBreakChar = Environment.NewLine,
    DumpStyle = DumpStyle.CSharp,
    ExcludeProperties = new string[]
    {
        //"Result"
    }
});

Console.WriteLine(str);

public class Tester
{
    public Tester(Expection expection)
    {
        Expection = expection;
        Actual = (Actual)expection;
    }

    public Expection Expection { get; }
    public Actual Actual { get; }
}

public class Expection
{
    public int Result { get; set; }
    public int[] Operands { get; set; }
    public static explicit operator Actual(Expection e) => new() { Operands = e.Operands };
}

public struct Actual
{
    public int Result => Operands.Sum();
    public int[] Operands { get; init; }
}

API Changes

I can think of three solutions for this

full qualified property names of the tree

Add an string interpretation which might point to the exact property
e.g. "Expection.Result" of the to-dump Tester object
Disadvantage: The '.' of object separation is very c#-specific and might lead to confusions

ExcludeReadonlyProperties

Add this to the DumpOptions to auto exclude all properties, which are readonly.
Disadvantage 1: what is readonly? everything which hasn't a public setter? what about objects with protect setter?
Disadvantage 2: You might include some readonly properties in the console-output, but exclude the rest of readonly members - this leads to the initial problem

Provide an interface for excluding

Something like

public interface IPropertyExcluder
{
    public bool ShouldExcludeProperty(in object obj, in string propertyName);
}

And an respective array on DumpOptions

So it could be used depending on the use case:

public class ActualPropertyExcluder : IPropertyExcluder
{
    public bool ShouldExcludeProperty(in object obj, in string propertyName)
        => (obj is Actual) && propertyName == nameof(Actual.Result);
}

ObjectDumper.Dump(tester, new DumpOptions()
{
    IndentChar = '\t',
    IndentSize = 1,
    LineBreakChar = Environment.NewLine,
    DumpStyle = DumpStyle.CSharp,
    ExcludeProperties = new IPropertyExcluder[]
    {
        new ActualPropertyExcluder()
    }
});

Advantage 1: The consumer can decide, which property should be excluded (depending on the use case)
Advantage 2: DumpOptions properties 'SetPropertiesOnly' could also be obsoleted
Disadvantage: Breaking Change when used ExcludeProperties with older versions