Urf*_*fly 5 c# arrays oop dynamic
我的任务很简单:我必须创建一个动态数组(这意味着它的大小可以在所有运行时间内更改)并使用不同类型的对象填充它(取决于用户输入,也在运行时).而且,我应该可以访问数组中每个对象的字段(和/或方法).显然,每种类型的字段都不同.简化结构:
public class Point {}
public class RightTriangle:Point { public double sideA, sideB; }
public class Circle:Point { public double radius; }
public class Cone:Circle { public double radius, height; }
Run Code Online (Sandbox Code Playgroud)
所以,你看:所有类都继承了一个基类.我知道,这些结构不合逻辑,但这不是我的选择.所以,我想要这个代码工作:
RightTriangle rt1 = new RightTriangle();
Cone cn1 = new Cone();
List<Point> objs = new List<Point>();
objs.Add(rt1);
sideA_tb.Text = objs[0].sideA.ToString();
Run Code Online (Sandbox Code Playgroud)
但事实并非如此.编译器说,没有sideA在Point.但是,这也不起作用:
public class Point { public double sideA, sideB, radius, height; }
public class RightTriangle:Point { public new double sideA, sideB; }
public class Circle:Point { public new double radius; }
public class Cone:Circle { public new double radius, height; }
Run Code Online (Sandbox Code Playgroud)
似乎来自实际类(rt1,它RightTriangle)的值不会覆盖Point类中的值,所以我有这样的东西:
List<Point> objs = new List<Point>();
objs.Add(rt1); // rt1 has fields 'sideA' = 4, 'sideB' = 5
sideA_tb.Text = rt1.sideA.ToString(); // Got 4, okay
sideA_tb.Text = objs[0].sideA.ToString(); // Got 0, what the?
Run Code Online (Sandbox Code Playgroud)
所以,基本上,我需要一个动态的链接数组,我想使用这个链接来访问对象字段和方法.另外,我知道我可以Get/Set在基类中编写一些函数并在子类中覆盖它们,但这似乎不是美容解决方案.提前谢谢,请忘记我的文盲(英语不是我的母语).
你几乎已经拥有它了(理论上):
public class Point { public virtual double sideA, sideB, radius, height; }
public class RightTriangle:Point { public override double sideA, sideB; }
Run Code Online (Sandbox Code Playgroud)
要使属性和方法在派生类中可重写,必须virtual在基类中声明它们。重写类必须将重写的属性/方法声明为override.
实际上,您不能创建字段virtual,只有属性和方法可以。所以你必须按如下方式更改你的代码:
public class Point {
public virtual double sideA { get; set; }
public virtual double sideB { get; set; }
public virtual double radius { get; set; }
public virtual double height { get; set; }
}
public class RightTriangle:Point {
public override double sideA { get; set; }
public override double sideB { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
但是,您不应该这样做,因为这是非常糟糕的设计。更多内容见下文。
new?当一个方法被重写时,将在运行时决定是调用重写的实现还是原始的实现。
因此,当一个方法接收 aPoint作为参数时,该Point实例实际上可能是 aRightTriangle或 a Circle。
Point p1 = new RightTriangle();
Point p2 = new Circle();
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,从使用and的代码的角度来看,p1和p2都是s 。然而,它们“下面”实际上是派生类的实例。Pointp1p2
p1.sideA因此,通过我的解决方案,例如,当您访问时,运行时将看起来“在下面”并检查到底Point是什么:它实际上是一个吗RightTriangle?然后检查是否存在重写的实现sideA并调用该实现。它实际上是一个吗Circle?然后进行相同的检查(这将失败)并调用sideA.
然而,限定符new还有其他作用。它不会覆盖方法,而是创建一个全新的方法(具有相同的名称),在编译时以不同的方式处理。
因此,通过您的解决方案,编译器会看到您创建了 aRightTriangle并将其存储在类型为 的变量中Point。例如,当您现在访问时,编译器将以从基p1.sideA类读取的方式编译此访问,因为您正在处理的实例变量具有基类型。sideA
如果您仍然想访问new实现,那么您使用的代码p1必须将其转换为正确的派生类型RightTriangle:
var w = ((RightTriangle)p1).sideA; // Gets correct value.
Run Code Online (Sandbox Code Playgroud)
正如您现在可能已经注意到的,使用new并不是一个好的解决方案。使用各种Points 的代码必须知道 的特定派生是否Point实现了字段new。此外,当它接收到一个实例时,它必须知道Point它实际上属于哪种实例。这将导致许多非常复杂的if语句else,这些语句检查正在处理什么类型的点p1并运行适当的行为。
使用virtual和override可以缓解最后一个问题,但前提是基类实现任何派生类实现的所有方法和属性。这基本上有点疯狂。你尝试过,我相信你注意到了,给出PointasideA和sideB a没有多大意义radius。如何才能Point有意义地实现这些属性呢?你abstract也不能制造它们,因为那时 aCircle也必须实现 asideA等等。
考虑一下您想用这个不同的点实例做什么。您将它们收集在一个列表中是有原因的:它们有一些共同点。此外,您还可以出于特定原因迭代此列表,对每个列表进行一些操作。但你到底想做什么?你能用抽象的方式描述一下吗?
也许您正在获取边长和半径来计算面积?然后给出Point一个virtual方法GetArea()并在每个派生类中重写它。方法GetArea()对所有派生类型都有意义,尽管每个派生类型的实现方式都不同。
您也可以创建GetArea()一种abstract方法来代替方法virtual。这意味着它根本没有在基类中实现,并且所有派生类型都被迫自己实现它。
也许您不想处理Point列表中的所有 s 而RightTriangle只想处理 s ?那么执行此操作的代码应该只接收RightTriangles:
public void HandleRightTriangles(IEnumerable<RightTriangle> rts)
{
// Can work with sideA and sideB directly here, because we know we only got triangles.
}
Run Code Online (Sandbox Code Playgroud)
调用它:
// using System.Linq;
HandleRightTriangles(objs.OfType<RightTriangle>());
Run Code Online (Sandbox Code Playgroud)