Mlezi
Mlezi

Reputation: 115

C# derived class and base constructor parameter logic

I have a base class:

public class Base
{
    public Base(X x ,Y y){
        this.x = x;
        this.y = y;
    }

    public X x{get;}
    public Y y{get;}
}

and a deriver:

public class Derive : Base
{
    public Derive(Z z, Q q) :Base (? ?)
    {
    }

    private void ConstructXY(Z z, Q q)
    {
        //Advanced logic for creating an X and a Y
        if(q.something == 5){
            this.x = new X(28); 
        }
        else{
            this.x = new X(25);
        }

        if(this.x.something == 25 && q == 9){
            this.y = new Y(1);
        }
        else{
            this.y = new Y(5)
        }
    }
}

Now I can't correctly call the base constuctor without the "advanced" logic. I used to be able to call ConstructXY() from Derive.ctor() and set x and y from there, this is no longer valid since I removed the x and y setters. My real-life scenario contains a lot more logic so I am not willing to create a ternary mess.

Upvotes: 2

Views: 1214

Answers (5)

TheEdge
TheEdge

Reputation: 9861

Why not declare your base class setters as private:

public class Base
{
    public Base(X x ,Y y){
        this.x = x;
        this.y = y;
    }

    public X x{get; private set;}
    public Y y{get; private set;}
}

That way you can still set them in the constructor, and they would not be settable outside?

Upvotes: 0

Perfect28
Perfect28

Reputation: 11317

You can call your "advanced" logic if it fits inside a static method

Here is an approach using Tuple available in C# 7 :

public class Base
{
    // This constructor was added to avoid calling twice ConstructXY
    public Base((X x, Y y) tuple) :
        this (tuple.x, tuple.y)
    {

    }

    public Base(X x, Y y)
    {
        this.x = x;
        this.y = y;
    }

    public X x { get; }
    public Y y { get; }
}

public class Derive : Base
{
    public Derive(Z z, Q q) : base(ConstructXY(z, q))
    {
    }

    private static (X x, Y y) ConstructXY(Z z, Q q)
    {
        X x;
        Y y;

        //Advanced logic for creating an X and a Y
        if (q.something == 5)
        {
            x = new X(5);
        }
        else
        {
            x = new X(25);
        }

        if (x.something == 25 && q == 9)
        {
            y = new Y(1);
        }
        else
        {
            y = new Y(5)
        }

        return (x, y);
    }
}

Upvotes: 5

Maarten
Maarten

Reputation: 22955

Use static methods to do the conversion. Like this.

public class Derive : Base
{
    public Derive(Z z, Q q) :base (ConvertToX(z, q), ConvertToY(z, q))
    {
    }

    private static X ConvertToX(Z z, Q q) {
        if(q.something == 5){
            return new X(28); 
        }
        return new X(25);
    }

    private static Y ConvertToY(Z z, Q q) {
        // TODO
    }
}

Upvotes: 0

poke
poke

Reputation: 388013

If you cannot change the access modifiers of X and Y in the base class so they are accessible for subclasses, then you will have to follow the contract, leaving the constructor the only possible place where you can set those members.

The only way to add more logic to calculate those values would be to use static methods, for example like this:

public class Derive : Base
{
    public Derive(Z z, Q q)
        : base(ConstructX(q), ConstructY(q, z))
    { }

    private static X ConstructX(Q q)
    {
        if (q.something == 5)
            return new X(28);
        else
            return new X(25);
    }

    private static Y ConstructY(Q q, Z z)
    {
        if (z.something == 25 && q.something == 9)
            return new Y(1);
        else
            return new Y(5);
    }
}

Since these are separate method calls, you cannot calculate both values “at once”, so you cannot make the result of Y depend on the result of X without redoing the calculation based on Z and Q again.

Another way you could solve this is by removing the public constructor on Derive altogether and provide a static factory method instead:

public class Derive : Base
{
    private Derive(X x, Y y)
        : base(x, y)
    { }

    public static Derive Create(Z z, Q q)
    {
        // here you can use your original logic to calculate X and Y
        X x = …
        Y y = …

        return new Derive(x, y);
    }
}

Depending on your complexity of those calculations, this might be the better solution. If you do need the original Z and Q values, just extend the private constructor to also take those and store them as well:

private Derive(Z z, Q q, X x, Y y)
    : base(x, y)
{
    this.Z = z;
    this.Q = q;
}

Upvotes: 2

TheLethalCoder
TheLethalCoder

Reputation: 6744

I don't really like this way of doing it and would avoid it if possible but you can use a ternary statement in your call to base:

public Derive(Z z, Q q)
    : base(new X(xCondition ? 28 : 25), new Y(yCondition ? 1 : 5))

Upvotes: 0

Related Questions