0

I am writing a class with member variables. I want to define default values for these fields and have to ability to override them with custom set values. I want to create some sort of class or struct in order to hold the data for these variables. I want someone using the class to have the ability to define all of the variables, or if they don't then the fields will be set to defaults (which I would define). Not sure of cleanest way to do this. This is what I have in mind but I'm not sure if I can do better:

public class ReportPageParams 
{
    public float Width { get; private set; }
    public float Height { get; private set; }
    public float LeftMargin { get; private set; }
    public float RightMargin { get; private set; }
    public float TopMargin { get; private set; }
    public float BottomMargin { get; private set; }

    //Constructor
    ReportPageParams(float pWidth, pHeight) 
    {
        Width = 52f
        Height = 52f
        //...
    }
}

public class ReportPage  
{
    //same fields as ReportPageParams plus some more
    private float _width, _height;
    private float _leftMargin;
    private float _rightMargin;
    //...

    ReportPage(ReportPageParams pCustomParams = null) 
    {
        if (pCustomParams != null) 
        {
            _width = pCustomParams.Width
            _height = pCustomParams.Height
            //...
        }
        else
        {
            //set fields to default values
        }
    }
}
Jason
  • 27
  • 1
  • 4

2 Answers2

1

Quick and dirty way: make your properties protected set, then create a class that is derived from your defaults. Only your base and derived classes will be able to make changes due to the properties being protected.

public class ReportPageParams
{
    public float Width { get; protected set; }
    public float Height { get; protected set; }
    public float LeftMargin { get; protected set; }
    public float RightMargin { get; protected set; }
    public float TopMargin { get; protected set; }
    public float BottomMargin { get; protected set; }

    //Constructor
    public ReportPageParams(float pWidth, float pHeight) 
    {
        Width = 52f
        Height = 52f
        //...
    }
}

public class ReportPageParamsWithLargeLeftMargin : ReportPageParams
{
    //Constructor
    public ReportPageParamsWithLargeLeftMargin(float pWidth, float pHeight)
        : base(pWidth, pHeight)
    {
        LeftMargin = 100;
    }
}

You'll find whatever values you set in the constructor for ReportPageParams will also appear in ReportPageParamsWithLargeLeftMargin. Because constructors are executed in order from the class closest to object to more specific classes, your changed defaults will be applied.

Alternatively, you can make the properties virtual and derive a class that overrides them with override. This puts you in charge of handling the property yourself.

A.Konzel
  • 1,920
  • 1
  • 13
  • 14
  • Hey thanks for the solution. I like this. If I could I would combine this with jdphenix's solution, but I'm not using C# 6 so I guess I'll just stick to a default constructor. – Jason Sep 21 '15 at 23:30
1

If you're using C# 6, you can use default values for auto implemented properties:

public sealed class ImAComplex
{
    public float TypeWith { get; private set; } = 20.0f;
    public float LotsOf { get; private set; } = 40.0f;
    public float PropertiesWith { get; private set; } = 20.0f;
    public float DefaultValues { get; private set; } = 10.0f;
    public float ThatGoOn { get; private set; } = 40.0f;
    public float AndOn { get; private set; } = 20.0f;
    public float ForALong { get; private set; } = 90.0f;
    public float TimeBefore { get; private set; } = 12.5f;
    public float FinallyEnding { get; private set; } = 80.0f;

    // ... 
}

If before that, there's still options to keep your public facing interface clean. You just have to do it the old fashioned way -

private float typeWith = 20.0f;
public float TypeWith
{
    get
    {
        return typeWith; 
    }
    private set
    {
        typeWith = value;
    }
}
// repeat ad nauseum

One technique I like is using static factory methods, which I like because you can name them based upon their function ("what kind of ImaComplex does I return" rather than "I return an ImAComplex"), for example:

    public static ImAComplex WithBigTimeBefore(float typeWith, float lotsOf)
    {
        return new ImAComplex
        {
            TypeWith = typeWith,
            LotsOf = lotsOf, 
            TimeBefore = BigValue 
        };
    }

If you use static factory methods, be sure to make the constructor private. It's as simple as private ImAComplex() { } to keep outside code from instantiating it.

Community
  • 1
  • 1
jdphenix
  • 15,022
  • 3
  • 41
  • 74