Использовать экземпляр класса в качестве значения по умолчанию в конструкторе другого класса?

#c# #constructor #default-arguments

Вопрос:

В C я могу сделать что-то вроде

 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 constamp; 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) {} };  

В C# я попытался:

 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) =gt; 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(  ValueTuplelt;float, float, floatgt; r = (1.0f, 0.0f, 0.0f),  ValueTuplelt;float, float, floatgt; u = (0.0f, 1.0f, 0.0f),   ValueTuplelt;float, float, floatgt; f = (0.0f, 0.0f, 1.0f))   {  dirs.r = r;  dirs.u = u;  dirs.f = f;  } }  

Ни то, ни другое не работает. Я знаю, что мне нужно было бы сделать что-то вроде

Как бы я поступил, если бы хотел?

Комментарии:

1. Как я вижу, нет особого смысла указывать некоторые из этих параметров и оставлять другие «по умолчанию» (поэтому укажите только r и u и оставьте f значение по умолчанию). Поэтому я бы просто добавил пустой конструктор, который приведет к матрице идентификаторов (или использовал для этого отдельное статическое свойство), и удалил все значения по умолчанию из текущего конструктора.

2. Верно и очень хорошо подмечено. Я просто пытаюсь понять обработку аргументов по умолчанию в C#. Кажется намного сложнее, чем в C .

Ответ №1:

Как говорит компилятор: значения по умолчанию должны быть выражены как константы времени компиляции, что: это не так. Вы можете использовать null значения, чтобы подразумевать эти значения, т. е.

 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); }  

Однако я бы сказал, что на самом деле это простой случай для a readonly struct (для обоих типов), а не class . Тогда у меня, вероятно, было бы просто статическое Identity свойство, которое возвращает идентификационную матрицу. Я также, вероятно ValueTuple , вообще не стал бы здесь использовать.

Пример того, что я имею в виду (примечание: использует функции C# 10):

 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 =gt; new();   public override string ToString()  =gt; $"({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()  =gt; $"[{R}, {U}, {F}]"; }  

Комментарии:

1. Спасибо, Марк. Не возражаете добавить пример кода для решения структуры только для чтения? Я хочу использовать значение, потому что векторные и матричные координаты должны быть изменяемыми.

2. @Razzupaltuff да, работаю над этим; Я бы сказал, математически, что вектор / матрица абсолютно не изменчивы, точно так же, как целое число, комплексное число, кватернион и т. Д. Не изменчивы. Возможно, вы захотите иметь возможность преобразовывать одну матрицу/вектор в другую, но это не одно и то же.

3. @Razzupaltuff также: с операторами композиции/декомпозиции: вам не нужна отдельная поддержка кортежей значений; вектор может действовать как кортеж.

4. Я исхожу из C , поэтому мне, возможно, придется приспособить свое мышление к другому мышлению для C#. Предположим, у меня есть вектор положения в каком-то объекте в 3D-игре. В C , чтобы изменить положение, я перезаписываю старые значения в векторе положения объекта. Так что этот вектор должен быть изменчивым. То же самое верно и для матрицы ориентации: когда меняется заголовок объекта, я хочу изменить его матрицу ориентации на месте.

5. @Razzupaltuff это зависит от вас — я покажу свой пример с readonly — вы можете удалить это