使用C#中的基类和派生类在数组中实例化对象

Instantiating objects in an array using base class and derived classes in C#

我试着为10名"运动"抽象类运动员的数组实例化对象,每个运动员都有名字和年龄,然后有两个派生类:"网球"运动员和"高尔夫"运动员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  class Program
    {
        static void Main(string[] args)
        {
            Sport[] athlete = new Sport[10];
            athlete[0] = new Tennis("John Smith", 18,"Tennis", 5.0, 92);
            athlete[1] = new Tennis("Lisa Townsend", 15,"Tennis");
            athlete[2] = new Tennis("Brian Mills", 17,"Tennis", 4.0, 83);
            athlete[3] = new Golf("Stacey Bell", 16,"Golf", 10, 20);
            athlete[4] = new Golf("Tom Spehr", 18,"Golf", 9, 12);
            athlete[5] = new Golf("Sam Calen", 14,"Golf");
            athlete[6] = new Tennis("Karen Strong", 17,"Tennis", 3.0, 78);
            athlete[7] = new Golf("Ken Able", 15,"Golf", 15, 16);
            athlete[8] = new Tennis("Troy Soni", 18,"Tennis", 4.5, 93);
            athlete[9] = new Golf("Toni Palmer", 17,"Golf", 8, 22);

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("{0}", athlete[i]);
            }    
        }
    }

我正在尝试像这样打印数组,但它输出不正确。另外,当我尝试将数据字段单独打印为

console.writeline("0_1",运动员[I].姓名,运动员[I].年龄)

我可以让它输出每个运动员的姓名和年龄,但如果我尝试添加其他字段,它们将不会输出。我应该将每个数组对象声明为"运动"而不是网球或高尔夫吗?

编辑:

这是体育课

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
abstract class Sport
{

    protected string name;
    protected int age;

    public Sport(string name, int age)
    {
        Name = name;
        Age = age;
    }
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
    public abstract void Performance();
 }

这里是派生的网球类(高尔夫类的结构与变量名的细微变化相同)

2


让我们把这整件事解决。现在是你职业生涯中学习良好习惯和实践的时候了。

2

体育运动?不,这个班不代表运动。这个类代表玩家。运动就是他们所玩的。你在你的Main中把当地的运动员命名为"运动员",这说明你在这里犯了一个错误。把这个叫做AthletePlayer之类的。比如说Player,因为派生类可以是TennisPlayerGolfPlayerGolfer

1
2
protected string name;
protected int age;

为什么要保护?你有公众的getter和setter包装这些!

完全消除支持字段。

1
2
3
4
5
6
7
8
9
10
11
public string Name
{
    get
    {
        return name;
    }
    set
    {
        name = value;
    }
}

这些是不必要的冗长;使用自动属性。

1
public int Age

年龄在不断地变化;生日不一样。存储生日,然后查看

如何根据生日计算年龄?

1
public abstract void Performance();

我不知道这是什么意思,但可能是错的。抽象方法通常是动词,但这是名词。这不是Perform吗?或是Play?您从派生类中省略了这个,所以让我们忽略它。

我们还想说我们玩什么运动。所以我们来做一个类型。把它们放在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Sport { Tennis, Golf }      
public abstract class Player
{
  // Consider making Name and Birthday get-only as well.
  // Is there any situation in which they change after construction?
  // If not, then *don't allow them to change*!
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
  public abstract Sport Sport { get; }
  public Player(string name, DateTime birthday)
  {
    this.Name = name;
    this.Birthday = birthday;
  }
}

更短更容易理解。现在我们来推导一些类型。我们将再次缩短属性,并添加ToString。

另外,Rating真的是双份的吗?使用双精度表示物理量,如高度或重量。我怀疑这是一个十进制数,而不是一个双精度数。如果将3.4的评级实际存储为3.399999999999是绝对错误的,那么double是错误的类型,decimal是正确的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public sealed class TennisPlayer : Player
{
  public override Sport Sport => Sport.Tennis;
  // Again, do these change after construction? If not
  // then remove the setters.
  public decimal Rating { get; set; }
  public int ServeSpeed { get; set; }
  public TennisPlayer(string name, DateTime birthday, decimal rating, int speed) : base(name, birthday)
  {
    this.Rating = rating;
    this.ServeSpeed = speed;
  }
  public override string ToString()
  {
    return @"Tennis player: {Name} {Birthday} {Rating} {ServeSpeed}";
  }
}

再一次,这么短,这么容易阅读。现在你对GolfPlayerGolfer或你所说的任何东西都做同样的事情。


如果我正确理解您的问题,您希望能够为您的类输出一个友好的字符串,在那里它们总是输出NameAge,然后为每个运动输出该运动的特定属性。

这可以通过覆盖ToString属性来实现。您可以在NameAge的基类中执行此操作,然后在每个单独的类中,您可以输出base.ToString以及任何特定的类属性。

例如,基类将输出名称和年龄:

1
2
3
4
5
6
7
8
abstract class Sport
{
    public override string ToString()
    {
        return string.Format("{0} {1}", Name, Age);
    }

    // Rest of class code omitted...

网球课还将输出一些其他领域:

1
2
3
4
5
6
7
8
9
class Tennis : Sport
{
    public override string ToString()
    {
        return string.Format("{0} [Sport: {1}] [Rating: {2}] [Serve Speed: {3}]",
            base.ToString(), Type, Rating, ServeSpeed);
    }

    // Rest of class code omitted...

然后您的输出将如下所示:

enter image description here

另外,请注意,Sport字符串对于Lisa Townsend是空的,因为用于实例化该实例的构造函数没有设置Type属性。