方法覆盖和多态

时间:2015-11-24 13:37:50

标签: c# inheritance

我正在尝试创建一个程序,允许用户进入酒店房间。该程序应检查房间是否空闲,然后分配一个免费房间(如果有的话)。我有多种房型,如单人房,双人房,双人房等等,都需要从基类Room继承。

这是我目前的代码。

public class Room 
{
    public static bool[] av = { false, true, false };

    public bool availability()
    {
        bool a = false;
        foreach (var roomAv in av)
        {
            a = a || roomAv;
        }
        return a;
    }

    public bool availability(int room)
    {
        return av[room];    
    }

    public int allocate()
    {
        if (availability())
        {
            int room = 0;
            while (!av[room])
            {
                room++;
            }
            av[room] = false;
            return room;
        }
        else
        {
            return -1;
        }
    }

    public static void roomStatus()
    {
        for (int i = 0; i < av.Length - 1; i++)
        {
            Console.WriteLine(i + av[i].ToString());
        }

    }
}

class SingleRoom
{

}

我在房间类中定义的功能需要可供所有不同房间类型使用,但每个房间类型都有自己独立的数组,说明它们是否可用。我怎样才能做到这一点?我如何为每个班级访问这些功能,但是在那里有自己独立的阵列而不是仅仅在'av&#39;像我目前一样的数组。

5 个答案:

答案 0 :(得分:2)

正如你所说,你是C#的新手,我建议重新考虑一下结构。你是面向对象的范例。你在想的是简单的旧C语言编程。

String

答案 1 :(得分:2)

  

我在房间类中定义的功能需要可以使用   所有不同的房间类型,但每个都拥有自己独立的阵列   说明它们是否可用。我怎么能这样做?

当您对变量使用static关键字时,您说变量属于类型本身而不是对象实例。引自MSDN Docs

  

使用静态修饰符声明属于的静态成员   类型本身而不是特定对象。

换句话说,您的数组由类Room“拥有”,而不是由Room创建的new类型的单个对象拥有。如果您希望每个对象实例拥有自己的私有成员变量,则需要删除static关键字。即。

public static bool[] av = { false, true, false };

应该是:

public bool[] av = { false, true, false };

请注意,这同样适用于方法名称,即,如果在方法上使用static关键字,则该方法由类/类型本身“拥有”,而不是单个对象实例。这意味着,您的roomStatus方法必须用作Room.roomStatus(),并且无法尝试new Room().roomStatus()

我实际上建议您删除数组并将其转换为属性,这样您就可以执行以下操作:

Room r = new SingleRoom();
if(r.IsAvailable)
{
    // ...
}

您还应该重构代码,以遵循方法,变量名称的.NET命名约定,并更好地使用面向对象。我认为Niraj Doshi's post是迈向这个方向的一个很好的一步。

由于您是C#的新手,我建议您掌握B. Wagner所着的 Effective C#一书。

更新 - 重构代码

这是我对重构代码的看法,它具有RoomManagerIRoom接口,IRoom接口的抽象实现,名为Room,代码和功能通用对于所有房间,具体SingleRoom用于更具体的类型,TextView类用于管理数据如何呈现/显示给用户(即基于文本的输出)。

请注意,这遵循模型 - 视图 - 控制器(MVC)设计模式,Room类是模型(即数据),TextView负责用于显示数据(即演示),主程序本身是控制器(即协调其他两个)。

Main Program

该程序只是添加一些房间,然后根据经理的能力显示每个房间的信息。

using System;
using System.Collections.Generic;

namespace HotelRoomManager
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            RoomManager mgr = new RoomManager (5);
            for (uint i = 0; i < mgr.Capacity; ++i)
                mgr.AddRoom (new SingleRoom (1, i + 1) );

            List<IRoom> rooms = mgr.GetAllRooms ();
            TextView view = new TextView ();
            view.RenderHeader ();
            view.RenderModels (rooms);

            mgr.RemoveAllRooms ();
        }
    }
}

IRoom接口

界面定义了一种类型,是所有房间的基础。接口用于与客户端定义契约,并且不依赖于实现细节,这使其成为一种良好的面向对象的实践。

using System;

namespace HotelRoomManager
{
    public enum BedType
    {
        Single,
        Double,
        Twin,
        Queen,
        King
    }

    public interface IRoom
    {
        BedType BedType { get; }
        uint Floor { get; }
        uint Number { get; }
        bool IsOccupied { get; set; }
    }
}

摘要Room

该会议室只包含所有会议室共有的代码,无论其个人详细信息如何。

using System;

namespace HotelRoomManager
{
    public abstract class Room : IRoom
    {
        private uint floor;
        private uint number;
        private bool occupied;

        public Room (uint floor, uint number)
        {
            this.floor = floor;
            this.number = number;
            occupied = false;
        }

        public uint Floor {
            get { return floor; }
        }

        public uint Number {
            get { return number; }
        }

        public abstract BedType BedType { get; }

        public bool IsOccupied {
            get { return occupied; }
            set { occupied = value; }
        }

        override public string ToString() {
            return "Room(floor=" + floor + ", number=" + number + ")";
        }
    }
}

具体SingleRoom

到目前为止,这个房间只需要报告其实际类型。除了已有的常用功能之外,它不需要做任何特殊的事情。

using System;

namespace HotelRoomManager
{
    public sealed class SingleRoom : Room
    {
        public SingleRoom (uint floor, uint number) : base(floor, number)
        {}

        override public BedType BedType {
            get { return BedType.Single; }
        }
    }
}

RoomManager

经理只是帮助跟踪所有房间,并提供简化的界面与集合进行互动。

using System;
using System.Collections.Generic;

namespace HotelRoomManager
{
    public class RoomManager
    {
        private List<IRoom> rooms;

        public RoomManager (uint capacity) {
            rooms = new List<IRoom> ();
            rooms.Capacity = (int) capacity;
        }

        public void AddRoom(IRoom room) {
            rooms.Add (room);
        }

        public void RemoveRoom(IRoom room) {
            rooms.Remove (room);
        }

        public List<IRoom> GetAllRooms() {
            return rooms;
        }

        public void RemoveAllRooms() {
            rooms.Clear ();
        }

        public uint Capacity {
            get { return (uint) rooms.Capacity; }
        }

    }
}

TextView

视图的唯一责任是决定如何将模型中的数据呈现给用户。此将数据本身与显示的数据分离,使您的系统更易于维护和扩展。您还可以使用多个视图,而不必在一个或另一个之间进行选择。

using System;
using System.Collections.Generic;
using System.Text;

namespace HotelRoomManager
{
    public class TextView
    {
        public TextView () {}

        public void RenderHeader() {
            Console.WriteLine ("Hotel Management System");
            Console.WriteLine ("-----------------------");
        }

        public void RenderModels(List<IRoom> rooms) {
            StringBuilder sb = new StringBuilder ();
            foreach (IRoom r in rooms) {
                sb.Append ("Floor   : " + r.Floor + "\n");
                sb.Append ("Number  : " + r.Number + "\n");
                sb.Append ("Bed     : " + r.BedType + "\n");
                sb.Append ("Occupied: " + (r.IsOccupied ? "Yes" : "No") + "\n\n");
            }
            Console.WriteLine (sb.ToString ());
        }
    }
}

输出

快速运行程序将产生以下输出:

Hotel Management System
-----------------------
Floor   : 1
Number  : 1
Bed     : Single
Occupied: No

Floor   : 1
Number  : 2
Bed     : Single
Occupied: No

Floor   : 1
Number  : 3
Bed     : Single
Occupied: No

Floor   : 1
Number  : 4
Bed     : Single
Occupied: No

Floor   : 1
Number  : 5
Bed     : Single
Occupied: No

答案 2 :(得分:1)

这很简单,而不是字段,使用属性:

obj_map

然后在你的派生类中:

public class Room 
{
    public virtual bool[] av { get; set; } = { false, true, false };

    //All of your functions remain unchanged except you need to remove static
}

继承的房间将设置将在基本功能中用于可用性的数组,因此您只需编写一次。

这是字段上属性的另一个优点,您可以在其中设置可以继承的属性。对原始代码唯一真正“错误”的是,数组和一些方法被声明为public class SingleRoom : Room { public override bool[] av { get; set; } = { true, true, false }; } ,这意味着它在所有类的实例中都是相同的。可用性应该是实例级字段/属性,而不是类型级别。

如果删除static并使您的派生类如下所示,原始代码可以正常工作:

static

答案 3 :(得分:0)

您将数组设置为static,这意味着对数组的所有访问都会到达同一个对象。

删除它,每个都有自己的。

根据评论,我们也应该从static方法中移除roomStatus标识符。

答案 4 :(得分:0)

这就是继承的用途。您可以创建abstract class并为所有具体类及其公共成员定义公共逻辑:

abstract class AbstractRoom 
{
    public bool[] av;

    public bool availability()
    {
        // some logic
    }

    public bool availability(int room)
    {
        // some logic   
    }

    public int allocate()
    {
        // some logic
    }

    public void roomStatus()
    {
        // some logic
    }
}

class MyConcreteRoom1 : AbstractRoom
{
}

class MyConcreteRoom2 : AbstractRoom
{
}

然后你可以做类似的事情:

List<AbstractRoom> rooms = new List<AbstractRoom>()
{
   new MyConcreteRoom1(), 
   new MyConcreteRoom2()
};

rooms[0].availability();
rooms[1].availability();