#c# #inheritance
Вопрос:
Я начал изучать C#, но что-то меня смущает. Допустим, у меня есть класс и 2 подкласса. У меня есть метод внутри родительского класса под названием Add(). Но когда я использую этот метод с подклассами, поскольку метод запрашивает «Toople», а не «Вектор» или «Точку», он выдает мне ошибку. Как я могу решить эту проблему? Есть ли способ разрешить вставку подклассов в метод или мне следует создавать новые методы для каждого отдельного подкласса? Большое спасибо.
using System;
namespace RayTracerChallange
{
public class Toople
{
public float x, y, z, w;
public Toople(float X, float Y, float Z, float W)
{
x = X;
y = Y;
z = Z;
w = W;
}
public Toople Add(Toople Toop)
{
return new Toople(this.x Toop.x, this.y Toop.y, this.z Toop.z, this.w Toop.w);
}
public class Point : Toople
{
Point(float X, float Y, float Z) : base(X, Y, Z, 1)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
public class Vector : Toople
{
Vector(float X, float Y, float Z) : base(X, Y, Z, 0)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
Комментарии:
1. Вы можете передать a
Point
или aVector
вToople.Add(Toople)
, но вы никогда не получите aToople
из этого метода, так как этот метод знает только, как сделатьTooples
2. @canton7 да, но разве нет способа разрешить возврат любого подкласса?
3. Это означало бы, что
Toople.Add
нужно было бы знать, как создать любой подклассToople
. Это невозможно, потому что любой может определить подкласс, и он может дать ему любые конструкторы, которые ему нравятся
Ответ №1:
Самое близкое, чего вы здесь добьетесь, — это сделав Toople
общее и абстрактное, возможно, сделав что-то вроде этого:
using System;
namespace RayTracerChallange
{
public abstract class ToopleBase<T> where T : ToopleBase<T>
{
public float x, y, z, w;
protected Toople(float X, float Y, float Z, float W)
{
x = X;
y = Y;
z = Z;
w = W;
}
protected abstract T MakeT(float X, float Y, float Z, float W);
public T Add(Toople Toop)
{
return makeT(this.x Toop.x, this.y Toop.y, this.z Toop.z, this.w Toop.w);
}
}
public class Toople : ToopleBase<Toople>
{
Toople(float X, float Y, float Z, float W) : base(X, Y, Z, W)
{
}
Toople MakeT(float X, float Y, float Z, float W)
{
return new Toople(X, Y, Z, W);
}
}
public class Point : ToopleBase<Point>
{
Point(float X, float Y, float Z) : base(X, Y, Z, 1)
{
}
protected override Point MakeT(float X, float Y, float Z, float W)
{
return new Point(X, Y, Z);
}
}
public class Vector : ToopleBase<Vector>
{
Vector(float X, float Y, float Z) : base(X, Y, Z, 0)
{
}
protected override Point MakeT(float X, float Y, float Z, float W)
{
return new Vector(X, Y, Z);
}
}
Пара вещей, которые следует отметить:
- Я не уверен, зачем вам это нужно, должно быть достаточно просто вернуть базовый класс
Toople
, если вы следуете принципу подстановки Лискова. (То есть остальная часть приложения должна иметь возможность ожидать, что любые подклассы будут вести себя так же, как их базовый класс, и даже не должны знать о существовании подклассов.) - Конструкторам дочерних классов не нужно задавать поля в родительском классе, так как родительский класс уже делает это.
Комментарии:
1. Когда добавляются 2 балла, добавляется ли также значение W или оно остается 1, потому что точки всегда имеют значение W, равное 1? И если добавить Вектор и Точку, каким должен быть тип результата? Всегда типа объявления?
2. Хорошая мысль. Универсальный
Add()
может принимать типT
. Или нет, в зависимости от точных требований; в конце концов, это надуманный пример. В операции нет ничего, что предполагало бы, что точки всегда имеют значение W, равное 1, хотя это просто инициализируется таким образом.
Ответ №2:
Я не полностью понимаю контекст, но, вероятно, у меня был бы только один класс Toople
без двух других ( Point
и Vector
), поскольку они не предоставляют никаких дополнительных функций (или я бы даже использовал a struc
, поскольку, похоже, речь идет о трассировке лучей, где скорость обычно имеет решающее значение).
Затем я бы добавил несколько операторов для обработки добавления, что-то вроде этого:
public struct Toople {
Toople(float x, float y, float z, float w) {
X = x;
Y = y;
Z = z;
W = w;
}
public static Toople CreatePoint(float x, float y, float z) {
return new Toople(x, y, z, 1);
}
public static Toople CreateVector(float x, float y, float z) {
return new Toople(x, y, z, 0);
}
public Single X { get; }
public Single Y { get; }
public Single Z { get; }
public Single W { get; }
public static Toople operator (Toople first, Toople second) {
checked {
return new Toople(first.X second.X, first.Y second.Y, first.Z second.Z, first.W second.W);
}
}
}