在 C# 中的另一个对象内实例化用户定义的对象

Posted

技术标签:

【中文标题】在 C# 中的另一个对象内实例化用户定义的对象【英文标题】:Instantiating User Defined Object inside Another Object in C# 【发布时间】:2021-04-26 11:44:54 【问题描述】:

我需要一些关于在应用程序中存储对象/数据的方式的帮助。我正在创建一个应用程序:创建机器人,为机器人分配任务,显示机器人信息和任务时间等。

我已经设置了多个不同的对象,以便在程序运行时在 Main 中实例化。然后用户将从类型列表中选择机器人类型。我无法将 botType 对象传递给 Robot 对象。我要求用户使用 int 选择 botType 1-6,然后我希望用户选择 int 来定义应该将哪个 botType 应用于机器人。 因为我在 Main 中初始化 BotTypes,然后使用不同的方法来 CreateRobot()。我无法将 botType 传递给 Robot 对象。我可以传递用户选择的整数,但这不会像我试图完成的那样将 botType 传递给 Robot。

这是我正在使用的类/构造函数:

  public class Robot
    
        //Store robot name
        public string botName  get; set; 

        //Store robot type
        public BotType botType  get; set; 

        //Store time to complete task
        public int timeElapsed  get; set; 

        public Robot(string BotName, BotType botType, int TimeElapsed)
        
            this.botName = BotName;
            this.botType = new BotType();
            timeElapsed = TimeElapsed;
        

    public class BotType
        
    
            //Type of robot
            public string TypeName  get; set; 
            //Type of task represented by number
            public int TaskType  get; set; 
    
            //contructor to set values
            public BotType (string typeName, int taskType)
            
                TypeName = typeName;
                TaskType = taskType;
            

然后是初始化对象的主要方法,但是当我尝试使用它们时,我无法像我想要的那样将用户定义的 int 转换为 BotType..

public class BotOMat
    
        public static List<Robot> botList = new List<Robot>();
        public static List<BotTask> botTaskMap = new List<BotTask>();
        public static List<BotType> botTypeMap = new List<BotType>();


        static void Main(string[] args)
        
            //initalize bot types
            BotType UNIPEDAL = new BotType("Unipedal", 1);
            BotType BIPEDAL = new BotType("Bipedal", 2);
            BotType QUADRUPEDAL = new BotType("Quadrupedal", 3);
            BotType ARACHNID = new BotType("Arachnid", 4);
            BotType RADIAL = new BotType("Radial", 5);
            BotType AERONAUTICAL = new BotType("Aeronautical", 6);

            //initialize bot tasks
            BotTask DISHES = new BotTask("Do the dishes", 1000, 0);
            BotTask SWEEP = new BotTask("Sweep the house", 3000, 0);
            BotTask LAUNDRY = new BotTask("Do the laundry", 10000, 0);
            BotTask RECYCLING = new BotTask("Take out the recycling", 4000, 0);
            BotTask SAMMICH = new BotTask("Make a sammich", 7000, 0);
            BotTask LAWN = new BotTask("Mow the lawn", 20000, 0);
            BotTask RAKE = new BotTask("Rake the leaves", 18000, 0);
            BotTask BATH = new BotTask("Give the dog a bath", 14500, 0);
            BotTask BAKE = new BotTask("Bake some cookies", 8000, 0);
            BotTask WASH = new BotTask("Wash the car", 20000, 0);

            var botTaskMap = new List<BotTask>  DISHES, SWEEP, LAUNDRY, RECYCLING, SAMMICH, LAWN, RAKE, BATH, BAKE, WASH ;
            var botTypeMap = new List<BotType>  UNIPEDAL, BIPEDAL, QUADRUPEDAL, ARACHNID, RADIAL, AERONAUTICAL ;

private static void createRobot()
        

            //Get robot name, add to list saving multiple names.
            Console.WriteLine("Enter robot name:");
            string botName = Console.ReadLine();

            //Get robot type
            Console.WriteLine("Enter robot type: (number)");
            int botType = Convert.ToInt32(Console.ReadLine());
            //botType = botTypeMap[botType];
            
            //boxing to convert int to BotType

            //error handling 
            if (botType < 1 || botType > 6)
            
                Console.WriteLine("Invalid input. Please enter number 1-6.");
                //BotType.TaskType = 0;
            

            //Add robot to the class storing robot information.
            if (botType > 1 || botType < 6)
            
                
                Robot aRobot = new Robot(botName, botType, 0);
                botList.Add(aRobot);
                aRobot.AssignBotTask();
                aRobot.CompleteTask();

            
            else
            
                MainMenu();
            

        

我可以将 BotType 作为整数传递给 Robot,也可以接收参数 2:无法从 int 转换为 BotOMat.BotType。每当我尝试使用 aRobot 写入任何输出时,控制台都会使用不是所需输出的受保护变量进行写入。

我应该在机器人类中创建一个机器人吗?然后我需要稍后将 BotTask(s) 分配给机器人......我相信这些使用关联类型关系,但如果对象没有在每个类中初始化。我不确定如何启动;例如:做菜的 Unipedal 机器人。

感谢您提前提供的任何帮助。我尽量做到描述性强,但不发布重复问题,因为我能找到的任何其他示例都比我认为我想要完成的要基本得多在这里。

【问题讨论】:

List 索引是从零开始的,所以试试botTypeMap[ botType - 1] 【参考方案1】:

下次发布问题时,您应该查看帮助,尤其是关于准备最小可重现示例的帮助。您的代码无法编译,您缺少类型、函数等。我将在下面展示的某些内容与您展示的内容不太相符,但它似乎确实符合您的意图(至少对我而言) .

我将从底层开始,然后逐步完善。您的 BotType 类只包含一个字符串和一个 int(整数递增)。我将不使用类,而是使用enum(您应该阅读这些内容)。枚举是值类型。在幕后,它们最终由一个简单的整数值类型(如int)表示,但随着它们包含的元数据,它们也有一个符号名称。所以:

public enum RobotType

    Unipedal = 1,
    Bipedal,
    Quadrupedal,
    Arachnid,
    Radial,
    Aeronautical,

如果我没有包含=1,那么Unipedal 会以默认值(零)开始。您将在下面看到为什么从一个开始有意义。枚举的好处是您可以将它们转换为整数,或表示其符号的字符串。您还可以将包含整数的字符串或包含符号名称的字符串解析为枚举实例:

var asInt = (int) RobotType.Unipedal;
var asString = RobotType.Unipedal.ToString();
bool isGoodParse = Enum.TryParse<RobotType>("1", out RobotType parsedFromInt);
isGoodParse = Enum.TryParse<RobotType>("Unipedal", out RobotType parsedFromString);

所有代码都按您预期的方式运行。

对您的任务类型做同样的事情是很诱人的。但名称不是有效的 C# 符号(它们有空格)。相反,我们将使用System.ComponentModel.DescriptionAttribute 为枚举值添加一些额外的元数据:

public enum RobotTaskName

    [Description("Do the dishes")]
    DoTheDishes = 1,
    [Description("Sweep the house")]
    SweepTheHouse,
    [Description("Do the laundry")]
    DoTheLaundry,
    [Description("Take out the recycling")]
    TakeOutTheRecycling,
    [Description("Make a sammich")]
    MakeASammich,
    [Description("Mow the lawn")]
    MowTheLawn,
    [Description("Rake the leaves")]
    RakeTheLeaves,
    [Description("Give the dog a bath")]
    GiveTheDogABath,
    [Description("Bake some cookies")]
    BakeSomeCookies,
    [Description("Wash the car")]
    WashTheCar,

但是,现在我们不能只在枚举值上调用ToString 来获取对应的名称。相反,我们将使用反射从 Description 属性中挖掘名称:

public static class RobotExtensions

    public static string GetDescription<T>(this T enumValue) where T : struct, Enum
    
        var enumInfo = typeof(T).GetField(enumValue.ToString());
        if (enumInfo != null)
        
            var attributes = enumInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attributes != null && attributes.Length > 0)
            
                return ((DescriptionAttribute[])attributes)[0].Description;
            
        
        //otherwise
        return enumValue.ToString();
    

该代码为枚举声明了一个扩展方法(您可以直接在枚举上调用它 - 任何枚举)。它获取枚举值的反映字段信息,检查它是否具有DescriptionAttribute。如果是,它会挖掘出描述并返回它。如果它无法获得该信息,它只会返回enumValue.ToString。结果:

RobotTaskName.DoTheDishes.GetDescription();   //returns "Do the dishes"
RobotType.Arachnid.GetDescription();          //returns "Arachnid"

这样做的另一个很酷的事情是,我们可以编写一些代码来提示用户输入特定的枚举值,检查它的有效性等:

public static T? GetResponseUsingEnum<T>(string prompt) where T : struct, Enum

    //Loop until a good answer (or no answer)
    while (true)
    
        Console.WriteLine($"prompt: Please enter one of:");
        var values = (T[])Enum.GetValues(typeof(T));
        foreach (var enumValue in values)
        
            var description = enumValue.GetDescription<T>();
            var intValue = Convert.ToInt32(enumValue);
            Console.WriteLine($"intValue: description");
        
        Console.Write(">> ");
        var response = Console.ReadLine();
        if (string.IsNullOrEmpty(response))
        
            return (T?)null;
        
        if (Enum.TryParse<T>(response, out var val))
        
            if (values.Contains(val))
            
                Console.WriteLine($"You answered: val");
                return val;
            
        
    

该代码显示特定枚举类型的所有值的整数和字符串表示形式,然后提示用户输入一个值。如果用户输入的值超出范围,那么它会重新提示他/她。如果用户只是点击 Enter,那么它会返回一个 null 值。否则,如果用户输入了一个有效的答案,它就会返回答案(不是作为一个整数,而是作为一个正确键入的枚举项。

现在底层已经完成了,让我们开始吧……

我不太清楚你的机器人任务类型是做什么的,或者额外的两个整数在哪里,所以我称它们为Num1Num2。我还添加了一个 Perform 函数来“执行”任务(将其回显到控制台)。

public class BotTask

    public RobotTaskName Name  get; set; 
    public string Description => Name.GetDescription();
    public int Num1  get; set; 
    public int Num2  get; set; 

    public BotTask(RobotTaskName name, int num1, int num2)
    
        Name = name;
        Num1 = num1;
        Num2 = num2;
    

    public void Perform()
    
        Console.WriteLine($"  - Peforming Task: Name.GetDescription() with Num1 and Num2");
    

如果您好奇,Description 属性是一个只读属性,它在基础RobotTaskName 枚举类型上调用GetDescription

然后我重新创建了您的 Robot 类型。我不知道您对 TimeElapsed 属性的意图是什么。但是,我将它从 int 更改为 TimeSpan - 因为,嗯,这就是 TimeSpans 的用途。

public class Robot

    public string BotName  get; set; 

    public RobotType BotType  get; set; 
    public string BotTypeDescription => BotType.GetDescription();

    public TimeSpan TimeElapsed  get; set; 

    private List<BotTask> _tasks = new List<BotTask>();
    public IEnumerable<BotTask> Tasks => _tasks;

    public Robot(string botName, RobotType botType, TimeSpan timeElapsed = default)
    
        this.BotName = botName;
        this.BotType = botType;
        TimeElapsed = timeElapsed;
    

    public void AddTask (BotTask task)
    
        _tasks.Add(task);
    

    public void Show()
    
        Console.WriteLine($"Robot: BotName, Type: BotTypeDescription, TimeElapsed: TimeElapsed");
        foreach (var task in Tasks)
        
            task.Perform();
        
    

请注意,我还添加了已分配给每个机器人的任务列表。这包括列表、向机器人添加任务的AddTask 方法等。

最后,我在Robot 类中添加了一个Run 方法,该方法会触发整个事件(从Main 调用它)。它允许您创建多个机器人并为每个机器人分配多个任务。它使用GetResponseUsingEnum 方法来获取机器人类型和任务类型。这意味着一致的用户界面和一些健康的代码重用(如果您在该功能中发现错误,则修复两个功能)。

public static void Run()

    var robotsList = new List<Robot>();
    //loop until there are no more robots
    while (true)
    

        Console.Write("Enter robot name: ");)
        var robotName = Console.ReadLine();
        if (string.IsNullOrEmpty(robotName))
        
            break;  //empty robot, time to list things out
        

        RobotType? robotType = null;
        while (!robotType.HasValue)
        
            robotType = GetResponseUsingEnum<RobotType>("Robots");
        

        var robot = new Robot(robotName, robotType.Value);
        robotsList.Add(robot);
        Console.WriteLine("Time to add some tasks for this robot");

        //get tasks - loop until no more tasks
        while (true)
        
            var taskName = GetResponseUsingEnum<RobotTaskName>("RobotTaskName");
            if (!taskName.HasValue)
            
                break;  //no more tasks
            
            var task = new BotTask(taskName.Value, 100, 200);
            robot.AddTask(task);
        
    

    //At this point, we have a fully populated list of robots, each with some tasks
    foreach (var robot in robotsList)
    
        robot.Show();
    

最后,如果你运行它,输出是这样的:

Enter robot name: Robby
Robots: Please enter one of:
1: Unipedal
2: Bipedal
3: Quadrupedal
4: Arachnid
5: Radial
6: Aeronautical
>> 2
You answered: Bipedal
Time to add some tasks for this robot
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>> 2
You answered: SweepTheHouse
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>> 3
You answered: DoTheLaundry
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>>
Enter robot name: SecondRobot
Robots: Please enter one of:
1: Unipedal
2: Bipedal
3: Quadrupedal
4: Arachnid
5: Radial
6: Aeronautical
>> 3
You answered: Quadrupedal
Time to add some tasks for this robot
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>> 8
You answered: GiveTheDogABath
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>> 10
You answered: WashTheCar
RobotTaskName: Please enter one of:
1: Do the dishes
2: Sweep the house
3: Do the laundry
4: Take out the recycling
5: Make a sammich
6: Mow the lawn
7: Rake the leaves
8: Give the dog a bath
9: Bake some cookies
10: Wash the car
>>
Enter robot name:
Robot: Robby, Type: Bipedal, TimeElapsed: 00:00:00
  - Peforming Task: Sweep the house with 100 and 200
  - Peforming Task: Do the laundry with 100 and 200
Robot: SecondRobot, Type: Quadrupedal, TimeElapsed: 00:00:00
  - Peforming Task: Give the dog a bath with 100 and 200
  - Peforming Task: Wash the car with 100 and 200

【讨论】:

以上是关于在 C# 中的另一个对象内实例化用户定义的对象的主要内容,如果未能解决你的问题,请参考以下文章

C# 关于 构造函数问题 关于对象实例化

C#中能否在类的内部实例化一个本类的对象?

通过 id 实例化 dto 对象,其中对象作为有效负载 C#

C#怎么实例化对象?具体是实例化啥?

C# 中的 Thread.Sleep():它是让实例化对象的线程休眠,还是让我从中调用方法的线程休眠?

java中的实例化对象有啥用???????