Razzupaltuff
Razzupaltuff

Reputation: 2301

Use a class instance as default value in another class' constructor?

In C++, I can do something like

class CVector3D {
public:
    float x, y, z;
    CVector3D (float x = 0.0f, float y = 0.0f, float z = 0.0f) : x(x), y(y), z(z) {}
    CVector3D (CVector3D const& other) { x = other.x, y = other.y, z = other.z; }
};

class CMatrix3D {
public:
    CVector3D r, u, f;
    CMatrix (r = CVector3D (1.0f, 0.0f, 0.0f), 
             u = CVector3D (0.0f, 1.0f, 0.0f), 
             f = CVector3D (0.0f, 0.0f, 1.0f)) 
        : r(r), u(u), f(f) {}
};

In C#, I tried:

public class CVector3D
{
    public (float x, float y, float z) coords;
    public CVector3D (float x = 0.0f, float y = 0.0f, float z = 0.0f) => coords = (x,y,z);
}

public class CMatrix3D
{
    public (CVector3D r, CVector3D u, CVector3D f) dirs;
    // variant 1.1
    public CMatrix3D (CVector3D r = CVector3D (1.0f, 0.0f, 0.0f), 
                      CVector3D u = CVector3D (0.0f, 1.0f, 0.0f), 
                      CVector3D f = CVector3D (0.0f, 0.0f, 1.0f))
    {
        dirs.r = r;
        dirs.u = u;
        dirs.f = f;
    }
    // variant 1.2
    public CMatrix3D (CVector3D r = new CVector3D (1.0f, 0.0f, 0.0f), 
                      CVector3D u = new CVector3D (0.0f, 1.0f, 0.0f), 
                      CVector3D f = new CVector3D (0.0f, 0.0f, 1.0f))
    {
        dirs.r = r;
        dirs.u = u;
        dirs.f = f;
    }
    // variant 2
    public CMatrix(
        ValueTuple<float, float, float> r = (1.0f, 0.0f, 0.0f),
        ValueTuple<float, float, float> u = (0.0f, 1.0f, 0.0f),         
        ValueTuple<float, float, float> f = (0.0f, 0.0f, 1.0f)) 
    {
        dirs.r = r;
        dirs.u = u;
        dirs.f = f;
    }
}

Neither of it works. I know that I would need to do something like

How would I go about what I want?

Upvotes: 0

Views: 81

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062780

As the compiler says: default values must be expressible as compile-time constants, which: this isn't. You could use the null values to imply those values, i.e.

public CMatrix3D(CVector3D r = null,
                  CVector3D u = null,
                  CVector3D f = null);
{
    dirs.r = r ?? new CVector3D(1.0f, 0.0f, 0.0f);
    dirs.u = u ?? new CVector3D(0.0f, 1.0f, 0.0f);
    dirs.f = f ?? new CVector3D(0.0f, 0.0f, 1.0f);
}

However, I would say that this is actually a prime case for a readonly struct (for both types), rather than class. I would then probably just have a static Identity property that returns the identity matrix. I also probably wouldn't use ValueTuple here at all.

Example of what I mean (note: uses C# 10 features):

using System;

var original = Matrix3D.Identity;
Console.WriteLine(original);

// show manipulation (note: you could also update the original value like this)
var delta = original with { F = new (2,2,2)};
Console.WriteLine(delta);

// to show tuple decomposition
Console.WriteLine(delta.F); // uses our type's ToString
var (r, u, f) = delta.F;
Console.WriteLine($"r={r}, u={u}, f={f}"); // do it ourselves


// record structs are C# 10, note!
readonly record struct Vector3D(float X, float Y, float Z)
{
    public static Vector3D Zero => new();

    public override string ToString()
        => $"({X}, {Y}, {Z})";
}

readonly record struct Matrix3D(Vector3D R, Vector3D U, Vector3D F)
{
    public static Matrix3D Zero { get; } = new();

    public static Matrix3D Identity { get; } = new(new(1, 0, 0), new(0, 1, 0), new(0, 0, 1));

    public override string ToString()
        => $"[{R}, {U}, {F}]";
}

Upvotes: 2

Related Questions