5

您能否对抽象类与继承的使用有一个几乎过于简单化的理解并帮助我,以便我能够真正理解这个概念以及如何实现?我有一个我正在尝试完成的项目,但不知道如何实施。我一直在和我的教授聊天并且被告知很多,说如果我想不通,我可能还没有准备好参加这门课程。我已经覆盖了预先要求的课程,但仍然无法理解这些概念。

为了澄清,我到目前为止所做的项目如下。我还没有填写狗/猫课程等。能给我指点一下吗。我不要求任何人给我“答案”。我只是不知道该去哪里。我参加在线课程,他与我的沟通努力一直令人不安。我刚刚完成了所有其他课程的 4.0,所以我愿意付出努力,但我对这些概念的理解以及如何实际应用它们迷失了方向。

有什么意见或帮助可以让我在这个项目中取得进一步进展吗?

我要实现的描述如下:

概述:

本练习的目的是演示接口、继承、抽象类和多态性的使用。您的任务是获取提供的程序外壳并添加适当的类和相应的类成员/方法,以使该程序正常运行。您不能对提供的任何代码进行更改,您只能添加您编写的类。尽管有很多方法可以让程序正常工作,但您必须使用能够演示接口、
继承、抽象类和多态性的技术。同样,为了清楚起见,您可以添加到提供的代码中,但不能更改或删除任何代码。提供的代码只需要很少的附加代码即可满足练习的要求。

如果您成功完成分配,您的程序在运行时应输出以下语句:

我的名字是斑点,我是一只狗

我叫菲利克斯,我是一只猫

要求:

1) 您必须有一个名为“Animal”的抽象基类,Dog 和 Cat 类从该基类派生而来。

2) Animal 基类必须从接口“IAnimal”派生,它是唯一应该从 IAnimal 派生的类。

3) 由于所有动物都有名字,而且名字不是狗或猫特有的属性,因此 Animal

基类应该是存储名称和实现 WhatIsMyName 获取属性的位置。

4) 您将需要创建仅从 Animal 基类派生的 Dog 和 Cat 类。

5) Dog 和 Cat 类应该实现 WhatAmI 获取属性并返回适当的字符串值。

您无法更改的代码:

using System;

namespace IT274_U2
{
    public interface IAnimal
    {
        string WhatAmI { get; }
        string WhatIsMyName { get; }
    }

    public class TesterClass
    {
        public static void DescribeAnimal(IAnimal animal)
        {
            Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI);
        }

        static void Main(string[] args)
        {
            Dog mydog = new Dog("Spot");
            Cat mycat = new Cat("Felix");
            DescribeAnimal(mydog);
            DescribeAnimal(mycat);
        }
    }
}

/////////////////////

到目前为止我写的代码:

using System;


namespace IT274_U2
{
    public interface IAnimal
    {
        string WhatAmI { get; }
        string WhatIsMyName { get; }
    }


    public class Dog
    {
        public abstract string WhatAmI
        {
            get;
            set;
        }
    }//end public class Dog

    public class Cat
    {
    public abstract string WhatIsMyName  
    {
        get;
        set;
    }
    }//end public class Cat

    public abstract class Animal : IAnimal
    {
    // fields
    protected string Dog;
    protected string Cat;

                  // implement WhatIsMyName 

    //properties
    public abstract String Dog
    {
        get;  
        set;
    }
    public abstract String Cat
    {
        get;
        set;
    }
    public abstract string WhatIsMyName();

    } //end public abstract class Animal


    public class TesterClass
    {
        public static void DescribeAnimal(IAnimal animal)
        {
            Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI);
        }

        static void Main(string[] args)
        {

            Dog mydog = new Dog("Spot");
            Cat mycat = new Cat("Felix");
            DescribeAnimal(mydog);
            DescribeAnimal(mycat);
        }
    }
}
4

9 回答 9

6

接口和抽象类之间的主要区别在于,在接口中,您只定义实现该接口的对象的公共方法和属性。抽象类提供了一些基本实现,但有一些“差距”——继承者需要实现的抽象方法。

我不会为你做功课,而是一个提示:Animal 类不应该包含任何特定于狗和猫的东西。

于 2009-03-09T17:35:12.313 回答
6

编辑:

我已经取出了每个类的代码主体 - 如果你想看到我的答案,请查看编辑修订 :)

首先我们定义接口

public interface IAnimal
{
    string WhatAmI { get; }
    string WhatIsMyName { get; }
}

任何实现此接口的类都必须实现这些属性。接口就像合约;实现接口的类同意提供接口方法、属性事件或索引器的实现。

接下来,我们需要定义你的抽象 Animal 类

public abstract class Animal : IAnimal
{
    //Removed for Training, See Edit for the code
}

类是抽象的这一事实表明该类仅用作其他类的基类。我们已经实现了接口的两个属性,并且还有一个私有字段来存储动物名称。此外,我们将WhatAmI属性访问器抽象化,以便我们可以在每个派生类中实现我们自己的特定属性访问器逻辑,并且还定义了一个接受字符串参数并将值分配给_name私有字段的构造函数。

现在,让我们定义我们的CatDog

public class Dog : Animal
{
    //Removed for Training, See Edit for the code
}

public class Cat : Animal
{
    //Removed for Training, See Edit for the code
}

这两个类都继承自Animal并且每个类都有一个构造函数,该构造函数定义一个字符串参数并将该参数作为参数传递给基本构造函数。此外,每个类都实现了自己的属性访问器WhatAmI,分别返回其类型的字符串。

对于其余的代码

public class Program
{
    public static void DescribeAnimal(IAnimal animal)
    {
        Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI);
    }

    static void Main(string[] args)
    {
        Dog mydog = new Dog("Spot");
        Cat mycat = new Cat("Felix");
        DescribeAnimal(mydog);
        DescribeAnimal(mycat);
        Console.ReadKey();
    }
}

静态方法DescribeAnimal接受 a作为参数,并为传入的IAnimal写出WhatIsMyName和属性访问器返回的值。WhatAmIIAnimal

由于Animalimplements IAnimaland both Dogand Catinherit from Animal,任何CatDog对象都可以作为参数传递给DescribeAnimal方法。

我希望我已经清楚地解释了这一点,如果有人觉得我的措辞需要收紧,请发表评论,我很乐意编辑我的答案。

于 2009-03-09T18:17:11.230 回答
3

你已经很接近了,但是这比它需要的更难。

我不想给你答案;)但这里有一些建议。

首先,您要创建 3 个类和 1 个接口。但是,我认为您可能缺少的一件事是您需要 3 种不同类型的对象(从“最少定义”到“最多定义”):

1) 接口
这是 IAnimal - 可以由任何可以像动物一样行动的东西来实现

2) 抽象基类 这是 Animal 类 - 任何是动物的东西都应该从 Animal 派生,但这些不能直接创建。如果你假装自己是上帝,你不会创造动物,你会创造狗、猫、松鼠或 FuzzyBunny

3) Animal 的具体实现这些是实际的类本身。这就是你创造的。狗或猫在你的情况下。

这里的技巧是你只能创建具体的类,但你可以使用 IAnimal 或 Animal(接口或抽象基类)来操作和使用任何动物(或者,在接口的情况下,任何像动物一样的东西)

于 2009-03-09T17:39:29.713 回答
2

一般来说:

  • 接口描述对象将响应的方法。这是对象承诺满足的契约。

  • 抽象类描述了基本功能,并将专门的功能赋予子类。

因此,基本上,当您希望对象本质上不同时,您可以使用接口,响应相同的特定方法。

当您需要某个类的专门版本时,您可以使用抽象类。

假设您想创建一个系统,其中任何类型的对象都可以由唯一的 id 标识,并且您不关心它们所属的类。

你可能有:

  • 动物

  • 运输

  • 电脑小工具。

  • 任何。

由于它们是不相关的主题,您可以选择实现和接口,假设:

public interface IIdentifiable 
{ 

      public long GetUniqueId();
}

并且所有想要满足这个契约的类都将实现那个接口。

public class IPod: IIdentifiable 
{
      public long GetUniqueId() 
      {
           return this.serialNum + this.otherId;
      }
}

public class Cat: IIdentifiable 
{
      public long GetUniqueId()
      { 
           return this.....
      }
}

iPod 和 Cat 两者具有非常不同的性质,但它们都可能响应将在目录系统中使用的“GetUniqueId()”方法。

那么它可以像这样使用:

    ...

    IIdentifiable ipod = new IPod(); 
    IIdentifiable gardfield = new Cat();

    store( ipod );
    store( gardfield );


    ....
    public void store( IIdentifiable object )  
    {

         long uniqueId = object.GetUniqueId();
        // save it to db or whatever.
    }

另一方面,您可能有一个抽象类来定义对象可能具有的所有常见行为,并让子类定义专门的版本。

  public abstract class Car 
  {
       // Common attributes between different cars
       private Tires[] tires; // 4 tires for most of them 
       private Wheel wheel; // 1 wheel for most of them.

        // this may be different depending on the car implementation.
       public abstract void move(); 
  }


  class ElectricCar: Car 
  {
      public void move()
      {
         startElectricEngine();
         connectBattery();
         deploySolarShields();
         trasnformEnertyToMovemetInWheels();
      }
  }

  class SteamCar: Car 
  {     
       public void move() 
       {
          fillWithWather();
          boilWater();
          waitForCorrectTemperature();
          keepWaiting();
          releasePreasure....
        }
   }

在这里,两种汽车,以不同的方式实现“移动”方法,但它们在基类中仍然共享共同的东西。

为了让事情更有趣,这两辆车可能也实现了 de IIdentifiable 接口,但这样做,它们只是承诺响应 GetUniqueId 方法,而不是作为汽车的性质。这就是为什么 Car 它自己可能无法实现该接口的原因。

当然,如果标识可以基于汽车可能具有的公共属性,则 GetIdentifiableId 可以由基类实现,子类将继承该方法。

// case 1 ... 每个子类都实现了接口

   public class ElectricCar: Car, IIdentifiable 
   {
       public void move()
       {
         .....
       }
       public long GetUniqueId() 
       { 
         ....
       }
   }

   public class SteamCar: Car, IIdentifiable 
   {
       public void move()
       {
         .....
       }
       public long GetUniqueId() 
       { 
         ....
       }
  }

情况2,基类实现接口,子类从中受益。

   public abstract class Car: IIdentifiable 
   {
       // common attributes here
       ...
       ...
       ...



       public abstract void move();
       public long GetUniqueId()
       {
          // compute the tires, wheel, and any other attribute 
          // and generate an unique id here.
       }
   }

   public class ElectricCar: Car
   {
       public void move()
       {
         .....
       }
   }

   public class SteamCar: Car
   {
       public void move()
       {
         .....
       }
  }

我希望这有帮助。

于 2009-03-09T18:50:55.920 回答
1
  1. 接口是一种契约。这是您要描述您将提供的功能的地方,没有任何实现细节

  2. 抽象类是一个类,其目的是在其子类之间共享实现细节。由于它在这里仅用于代码共享/分解目的,因此无法实例化

  3. 您的实际类将从您的抽象类继承,并在需要时使用抽象类中共享的代码来实现其特定于类的功能。

于 2009-03-09T17:41:40.253 回答
1

老实说,无论这是否是一个家庭作业问题,我都让业内不知道这一点的人感到害怕。因此我会回答。

接口抽象实现,抽象类也是如此。没有“vs”,因为您也可以创建一个实现接口的抽象类。所以不要以为他们在互相打仗。

因此,当您不希望消费者对实现了解太多时,可以使用 EITHER。接口在这项工作中要好一些,因为它没有实现,它只是说明消费者可以按下哪些按钮,然后将返回的值发送到抽象类可能声明的更多信息(甚至更多!) . 因此,如果您只是将这一点纳入船上,那么您实际上只需要接口。因此,第二点:

当您想在接口的两个不同实现之间共享公共代码时,使用抽象类。在这种情况下,两个具体的实现都继承自实现接口的抽象类。

简单的例子是 IDataStore。SavingToATextFile 数据存储只是一个实现 IDataStore 的类。但是 MsSqlDataStore 和 MySqlDataStore 将共享公共代码。它们都将继承自实现 IDataStore 的抽象类 SqlDataStore。

于 2009-03-09T18:23:15.363 回答
0

抽象类:为派生类建立基础,它们为所有派生类提供契约。它强制执行层次结构

接口:

接口不是类,它是方法的定义。

一个类可以继承多个接口,但只能继承一个抽象类。

我希望这会有所帮助

于 2009-03-09T17:36:09.580 回答
0

基本上,接口定义了所有实现者必须提供的“契约”(即一组操作/属性)。在这种情况下,IAnimal 接口需要 WhatAmI 和 WhatIsMyName 属性。抽象类可能提供某些功能,但也会留下一些必须由其子类实现的操作。

在您的示例中,Animal 基类能够提供 WhatIsMyName 功能,因为“名称”是所有动物的属性。但是,它不能提供“WhatAmI”属性,因为只有特定的子类知道它们是什么“类型”。

您发布的示例代码的问题是,动物类知道它的子类,它不应该知道

于 2009-03-09T17:44:25.630 回答
0

另一个建议 - (稍微偏离主题,但仍然相关)

出于学习目的,我建议避免使用自动属性。如果您明确实施它们,它将帮助您了解正在发生的事情。

例如,不要这样做:

class SomeClass
{
   public string MyProperty
   {
       get;
       set;
   }
}

尝试自己实现:

class SomeClass
{
    public string MyProperty
    {
        get
        {
             return "MyValue"; // Probably a private field
        }
        set
        {
             // myField = value; or something like that
        }
}

我提到这一点是因为它会在这种特定情况下为您提供帮助。由于您使用的是自动属性,因此编译器会为您“填补空白”,在您的情况下,我认为它会阻止您获得一些非常有用的编译器错误。当试图理解这些概念是如何工作的时,自己动手通常会更容易,而不是更难。

于 2009-03-09T18:11:09.683 回答