Аргументы метода C# и типы подклассов

#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 или a Vector в Toople.Add(Toople) , но вы никогда не получите a Toople из этого метода, так как этот метод знает только, как сделать 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);
        }
    }

}