当遍历从同一类继承的不同对象时,C#体系结构问题



对不起这个标题,我真的找不到明确的方法来解释它。

我正在做一个生成SpheresTriangles的项目,我让它们从类Objet继承并存储在List<Objet>
现在我需要迭代这些对象(使用foreach(,并且我希望在这个循环中使用它们的函数getNormale(???)
问题是类Sphere需要两个参数,而类Triangle不。。。

我找到了两种方法,但我不知道该用哪一种,如果有更好的方法:

  1. 将抽象函数getNormale(u, v)放在类Objet中并覆盖它,问题是Triangle.getNormale(u,v)实际上并不需要这些参数
  2. SphereTriangle放在两个不同的列表中,List<Sphere>List<Triangle>分别遍历它们,并分别调用getNormale(u,v)getNormale()。我真的不喜欢这个答案,因为如果我决定添加继承Objet的其他类,我将需要修改这个循环

由于getNormaleSphere需要2个参数,为Triangle需要0个参数,因此它们之间的关系不足以将getNormale的实现存储在Objet类中。如果有多个类允许getNormale的参数列表为空,则可以为此使用一个子类,同样,如果有多个子类允许getNormale的2个参数,也可以是其自己的子类。

您可以使用is运算符来区分这两个对象,并在对它们调用适当的getNormale方法之前将它们强制转换为相应的类

foreach(var shape in shapes)
{
string normale;
if(shape is Triangle)
{
normale = ((Triange)shape).getNormale();
}
else if(shape is Sphere)
{
normale = ((Sphere)shape).getNormale(u,v);
}
Console.WriteLine($"Normale: {normale}");
}

或者,如果uv是与对象本身相关的参数(可能u是周长,v是半径(,并且您可以在初始化时知道它们,那么它们应该作为字段存储在Sphere类中。在这种情况下,您可以在没有任何参数的情况下调用getNormale,因为您的Sphere类已经可以访问uv

您的目标应该是能够将集合迭代为基类Objet,并能够对每个对象调用GetNormal(((或者使用条件逻辑来确定您是否有Sphere,并从更专业的派生类中调用更具体的方法(。

在C#中,对基类中可以重写的方法使用"virtual"关键字,并使用"override"关键字声明派生类中的方法(如果派生类中有一个具有不同签名的类似方法,则无需重写,因为这将被视为单独的方法(。

using System;
using System.Collections.Generic;

public class Program
{
public static void Main()
{
Objet o1 = new Objet("o");
Sphere s1 = new Sphere("s", 10);
Triangle t1 = new Triangle("t", 5);
//  The collection type is Objet, but you can add derived types
List<Objet> Objets = new List<Objet> { o1, s1, t1 };
foreach (Objet o in Objets)
{
if (o.GetType().Name == "Sphere")
{
//  Could use 'if (o is Sphere)' ;)
//  Special treatment for spheres
Sphere s = (Sphere)o;
s.GetNormale("Foo", "Bar");
}
else
{
//  Could just call this for every item
o.GetNormale();
}
}
}
public class Objet
{
public string Name {get; set;}

public Objet()
{
}

public Objet(string name)
{
Name = name;    
}
public virtual void GetNormale() =>
Console.WriteLine(String.Format("Objet prints: {0}", Name));
}
public class Triangle : Objet
{
public Triangle(string name, int side) : base(name)
{
Side = side;
}

public int Side {get; set;}

//  Override the base class method
public override void GetNormale() => 
Console.WriteLine(String.Format("Triangle prints: {0}", Side));
}
public class Sphere : Objet
{   
public Sphere(string name, int circumference) : base(name)
{
Circumference = circumference;
}

public int Circumference {get; set;}
//  Method signature is different, no override required
public void GetNormale(string u, string v) =>
Console.WriteLine(String.Format("Sphere prints: {0} & {1} & {2}", Circumference, u, v));
}
}
//  Output
//  Objet prints: o
//  Sphere prints: 10 & Foo & Bar
//  Triangle prints: 5

这被称为"多态性",根据具体情况,可以将对象称为基类或派生类。

试着阅读"SOLID软件设计原则",它为类继承提供了最佳实践建议。

您可以检查cast并以某种方式调用它。不需要在祖先中具有虚拟版本

static void Main(string[] args)
{
List<Shape> shapes = new List<Shape>();
shapes.Add(new Triangle());
shapes.Add(new Sphere());
foreach (var shape in shapes)
{
_ = (shape is Triangle) ?
((Triangle)shape).GetNormale() :
((Sphere)shape).GetNormale(0, 0);
}
Console.ReadLine();
}

首先,您确实应该创建一个名为shape的抽象类,SpheresTriangles都应该从中继承。然后向shape添加创建List<shape>而不是List<Object>的方法getNormale()。调用基类Object是非常令人困惑的,您应该始终使基类尽可能准确地描述派生的

现在,为了问题本身。用不同的参数重写一个方法是不可能的,这就是你真正要问的问题。这就是为什么我建议将参数放入构造函数中,并允许通过set方法/属性更改它们,这实际上取决于你的参数的使用。

这是处理此类问题的常用方法。

如果这些参数不适合作为classes属性,那么您就缺少了。您需要放弃将它们一起调用,而是坚持使用List<object>,检查每个对象的类型,并相应地获得Normale,每个类型都有自己的方法和参数。切勿使用冗余参数。这可能非常令人困惑。

double normale;
ICollection<Object> shapes = new List<object>();
//add you shapes here
foreach var shape in shapes{
if(shape is Spheres)
{
normale = ((Spheres)shape).getNormale(u, v);
}
else
{
normale = ((Triangles )shape).getNormale();
}
}

最新更新