我正在尝试创建一个程序,允许用户进入酒店房间。该程序应检查房间是否空闲,然后分配一个免费房间(如果有的话)。我有多种房型,如单人房,双人房,双人房等等,都需要从基类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;像我目前一样的数组。
答案 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#一书。
这是我对重构代码的看法,它具有RoomManager
,IRoom
接口,IRoom
接口的抽象实现,名为Room
,代码和功能通用对于所有房间,具体SingleRoom
用于更具体的类型,TextView
类用于管理数据如何呈现/显示给用户(即基于文本的输出)。
请注意,这遵循模型 - 视图 - 控制器(MVC)设计模式,Room
类是模型(即数据),TextView
负责用于显示数据(即演示),主程序本身是控制器(即协调其他两个)。
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();