解决 有关在类中实例化对象的问题

马修

成员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
大家好,

我有一个问题想通过一个例子来阐明。

我有一个俱乐部班和一个TennisPlayer班。一个俱乐部可以有许多网球运动员作为该俱乐部的成员,而一个网球运动员一次只能是一个俱乐部的成员。
我的课看起来像这样。我简化了此示例的类。

C#:
public class Club
    {
        private List<TennisPlayer> members;

        public Club()
        {
            members = new List<TennisPlayer>();
        }

        public void AddTennisPlayerAsMember(TennisPlayer tennisPlayer)
        {
            members.Add(tennisPlayer);
            tennisPlayer.Club = this;
        }
    }


C#:
public class TennisPlayer
    {
        public string Name;
        public Club Club;

        public TennisPlayer(string name, Club club)
        {
            Name = name;
            club.AddTennisPlayerAsMember(this);
        }
    }

所以我的问题是,在TennisPlayer类中,野外俱乐部没有像Club club = new Club()这样的东西吗?
因为如果我这样做,我实例化的每个TennisPlayer对象都会有自己的Club对象,这是不合逻辑的,因为每个俱乐部只有一个Club对象,就像现实生活中没有同一个俱乐部的副本一样,对吗? ?
 

约翰·H

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
1,076
地点
挪威
编程经验
10+
在TennisPlayer类中,野外俱乐部没有像Club club = new Club()这样的东西吗?
没错,玩家不应成为创建新俱乐部的界面。

同样,在玩家类中,构造函数通过参数设置club。请注意,如果未分配任何俱乐部,则参数可能为null,这将导致AddTennisPlayerAsMember调用上的NullReference异常,可以使用空条件运算符轻松处理该异常: 成员访问运算符和表达式-C#参考
C#:
club?.AddTennisPlayerAsMember(this);
 

马修

成员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
没错,玩家不应成为创建新俱乐部的界面。

同样,在玩家类中,构造函数通过参数设置club。请注意,如果未分配任何俱乐部,则参数可能为null,这将导致AddTennisPlayerAsMember调用上的NullReference异常,可以使用空条件运算符轻松处理该异常: 成员访问运算符和表达式-C#参考
C#:
club?.AddTennisPlayerAsMember(this);
感谢您的回答。现在,我知道自己的工作进展顺利。

现在,假设俱乐部类具有一个额外的字段名称(俱乐部名称)。
想象一下,我创建了一个Club对象“ c”和一个Tennisplayer对象“ t”。通过使用t的构造函数,我将t链接到c。
现在,可以通过t到达c并访问名称字段(t.Club.Name),这样就可以了。如果我想知道网球运动员对象的俱乐部名称,这将很方便。
但是现在我也可以通过t(t.Club.AddTennisPlayerAsMember)到达方法AddTennisPlayerAsMember,这还不行。
因为现在我可以通过使用Tennisplayer对象来更改俱乐部。
因此,我的想法是将TennisPlayer类中的野外俱乐部设为私有,因此我无法在他的班级之外访问该俱乐部,但现在我无法通过TennisPlayer对象获得俱乐部名称。
您应该如何处理这个“问题”。我可以在TennisPlayer类中设置一个俱乐部名称,并为其指定一个值,但看起来有点奇怪,或者这很常见吗?
 

约翰·H

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
1,076
地点
挪威
编程经验
10+
字段应始终是私有的,请使用属性将字段值(对象状态)暴露给外部。属性也可以声明为只读。
 

羊皮

知名会员
已加入
2018年9月5日
留言内容
1,982
编程经验
10+
您打算如何定义每个网球运动员所属的俱乐部?在您的俱乐部对象中,您没有类俱乐部对象代表哪个俱乐部的名称。如果您有多个俱乐部,这将是一个问题。

从TennisPlayer类中删除第4行。它在那里没有目标,并且打破了单一责任目标的规则。

您应该考虑利用抽象类作为您其他分类的基础。我先坐下来,然后写在纸上。然后,当您想到它时,用代码将其写出。
 

马修

成员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
字段应始终是私有的,请使用属性将字段值(对象状态)暴露给外部。属性也可以声明为只读。

我知道我必须使用属性,但是我无法确保俱乐部类的列表成员的安全。

C#:
public class Club
    {
        public string Name;
        public List<TennisPlayer> Members { get; }

        public Club(string name)
        {
            Name = name;
            Members = new List<TennisPlayer>();
        }

        public void AddMember(TennisPlayer tennisPlayer)
        {
            Members.Add(tennisPlayer);
            tennisPlayer.Club = this;
        }

    }

C#:
public class TennisPlayer
    {
        public Club Club { get; set; }

        public TennisPlayer(Club club)
        {
            Club.AddMember(this);
        }
    }

C#:
class Program
    {
        static void Main(string[] args)
        {
            Club club = new Club("Laagland");
            TennisPlayer tennisPlayer = new TennisPlayer(club);
            tennisPlayer.Club.Members.Add(tennisPlayer);
        }
    }

如您所见,通过Tennisplayer对象,我可以到达并更改球员所属俱乐部的会员名单。
但是我不能将成员列表声明为私有,因为那样我就不能通过club对象对其进行排序,这应该没问题。
 

约翰·H

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
1,076
地点
挪威
编程经验
10+
public List<TennisPlayer> Members { get; }
这意味着不能从外部设置列表对象,仍然可以从外部自由操作列表的内容。解决此问题的一种方法是将列表保留在私有字段中,使用公共方法添加/删除成员,以及一个只读属性,该属性仅返回列表内容的副本(作为数组)。
 

约翰·H

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
1,076
地点
挪威
编程经验
10+
一个网球运动员一次只能成为一个俱乐部的成员
可以通过将球员添加到俱乐部的方法来减轻这种限制,如果球员已经拥有俱乐部,则该方法还可以首先将球员从现有俱乐部中删除。
pseudo clode:
tennisPlayer.Club?.RemovePlayer(tennisPlayer);
 

马修

成员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
这意味着不能从外部设置列表对象,仍然可以从外部自由操作列表的内容。解决此问题的一种方法是将列表保留在私有字段中,使用公共方法添加/删除成员,以及一个只读属性,该属性仅返回列表内容的副本(作为数组)。
但是网球员仍然有俱乐部对象,对吗?因此,如果我将成员列表保留在私有字段中,那么我不再可以直接更改列表是正确的,但是现在我可以使用公共方法添加/删除成员来更改列表。这样,TennisPlayer对象仍然可以更改球杆。
 

羊皮

知名会员
已加入
2018年9月5日
留言内容
1,982
编程经验
10+
让您的每个班级都有一个关注点。一个目的。
网球运动员不应在其所在的俱乐部上保留任何信息。
俱乐部不应保留其俱乐部中网球运动员的任何数据。
 

马修

成员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
让您的每个班级都有一个关注点。一个目的。
网球运动员不应在其所在的俱乐部上保留任何信息。
俱乐部不应保留其俱乐部中网球运动员的任何数据。
我是SRP的新手,还是一个新手 :)。但是想象一下一个班级的作者和一个班级的书。一本书确实有作者,所以一本书类包含一个作者对象,对吗?
根据SRP,我可能不会在书中声明作者对象吗?对于OOP的初学者来说,我确实感到很奇怪。
 

羊皮

知名会员
已加入
2018年9月5日
留言内容
1,982
编程经验
10+
但是想象一下一个班级的作者和一个班级的书。一本书确实有作者,所以一本书类包含一个作者对象,对吗?
这就是为什么我们有时使用抽象。但是,如果您要坚持使用SRP,那么您将希望严格按照课程的目的对课程进行保留。在您的情况下,它们似乎是有关A)俱乐部和B)网球运动员的详细信息。

您可以通过多种方式来执行自己的工作,包括使用继承来从另一个类继承对象值,同时保持相对于SRP的状态。

您可能会写它的一种方法是演示以下代码。创建一个Club对象,仅使用创建Club对象所需的对象,在本示例中,我将使用三个属性。代码带有注释,因此您可以将其悬停在VS中,它会告诉您每个部分在做什么:
C#:
    public class Club
    {
        public string ClubName { get; set; }
        public string ClubAddress { get; set; }
        public string ClubPhoneNumber { get; set; }
        /// <summary>
        /// The Club class is inisialised with a new instance of a club object and its details are provided upon instanciation.
        /// </summary>
        /// <param name="clubName">The name of the club is provided here</param>
        /// <param name="clubAddress">The club address is provided here</param>
        /// <param name="clubPhoneNumber">The clubs phone number is provided here</param>
        public Club(string clubName, string clubAddress, string clubPhoneNumber)
        {
            ClubName = clubName ?? throw new ArgumentNullException(nameof(clubName));
            ClubAddress = clubAddress ?? throw new ArgumentNullException(nameof(clubAddress));
            ClubPhoneNumber = clubPhoneNumber ?? throw new ArgumentNullException(nameof(clubPhoneNumber));
        }
    }
接下来,您需要网球运动员提供的同样的东西。名称,地址,甚至是电话号码...
C#:
    public class TennisPlayer
    {
        public string PlayerName { get; set; }
        public string PlayerAddress { get; set; }
        public string PlayerPhoneNumber { get; set; }
        /// <summary>
        /// The TennisPlayer class is inisialised with a new instance of a TennisPlayer object and its details are provided upon instanciation.
        /// </summary>
        /// <param name="playerName">The name of your tennis player</param>
        /// <param name="playerAddress">The address of your tennis player</param>
        /// <param name="playerPhoneNumber">The phone number of your tennis player</param>
        public TennisPlayer(string playerName, string playerAddress, string playerPhoneNumber)
        {
            PlayerName = playerName ?? throw new ArgumentNullException(nameof(playerName));
            PlayerAddress = playerAddress ?? throw new ArgumentNullException(nameof(playerAddress));
            PlayerPhoneNumber = playerPhoneNumber ?? throw new ArgumentNullException(nameof(playerPhoneNumber));
        }
    }
接下来,您需要某种类型的控制器类。您可以在其中添加和删除俱乐部中的网球运动员的东西。我还建议为所有数据添加一个模型类。然后,该模型将绑定到您的UI。但这在本示例中未实现。这里的主要目的是演示保持类对象相对于它们表示的内容:
C#:
    /// <summary>
    /// Here you can use an additional class to work with the objects of your other classes; such as Club, and TenniPlayer.
    /// This class can add your tennis players to a list of tuple TennisPlayer, Club. When this class is first instanciated,
    /// keep a copy of the instanciated object to pass around your application. New instances of this class will create a new instance of _PlayerClubPair.
    /// </summary>
    public class WithMembersOfClubs
    {
        /// <summary>
        /// This list keeps track of your tenis players and the clubs they are in.
        /// </summary>
        private List<Tuple<TennisPlayer, Club>> _PlayerClubPair = new List<Tuple<TennisPlayer, Club>>();
        /// <summary>
        /// This method will add players to your _PlayerClubPair list and store the values of your tennis player and club in a tuple<Tuple<TennisPlayer, Club>>.
        /// </summary>
        /// <param name="tennisPlayer">This is where you pass in the tennis player object</param>
        /// <param name="clubName">This is where you pass in the club object</param>
        public void AddTennisPlayerAsMember(TennisPlayer tennisPlayer, Club clubName)
        {
            _PlayerClubPair.Add(Tuple.Create(tennisPlayer, clubName));
        }
        /// <summary>
        /// This method will remove all references of a TennisPlayer object where the tuple item variable matches the tennisPlayer.Player name variable.
        /// </summary>
        /// <param name="tennisPlayer">This is where you pass in the tennis player you wish to remove</param>
        public void RemoveTennisPlayerAsMember(TennisPlayer tennisPlayer)
        {
            _PlayerClubPair.RemoveAll(t => t.Item1.PlayerName == tennisPlayer.PlayerName);
        }
        /// <summary>
        /// This method allows you to check the number of members in a given club object.
        /// </summary>
        /// <param name="clubName">The name of the club you want to check the number of players in</param>
        /// <param name="number">The number variable will be used to count how many members are in a given club</param>
        /// <returns></returns>
        public int NumberOfMembers_In(string clubName, int number)
        {
            number += (_PlayerClubPair.Where(club => club.Item2.ClubName == clubName)).Count();
            return number;
        }
要使用此代码,您只需要调用俱乐部和Tennisplayer对象的构造函数,并将它们传递给数据在其中的另一个类即可。"managed"就像控制器将在Web应用程序中管理数据一样,您通常会从那里更新一个模型,然后正如我所说,该模型将绑定到您的UI,在其中您的UI会根据输入的新数据自动进行更新。运行代码的示例如下:
C#:
            Club club = new Club("Bondi Club", "33 Bondibeach Park", "0123456789");
            TennisPlayer tennisPlayer1 = new TennisPlayer("Sheepings", "Sheep ville", "0234567890");

            WithMembersOfClubs membersOfClubs = new WithMembersOfClubs();
            membersOfClubs.AddTennisPlayerAsMember(tennisPlayer1, club);
            TennisPlayer tennisPlayer2 = new TennisPlayer("Bob", "Bob ville", "3214567890");
            membersOfClubs.AddTennisPlayerAsMember(tennisPlayer2, club);

            Debug.WriteLine(string.Join(" ", $"{club.ClubName} has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));

            membersOfClubs.RemoveTennisPlayerAsMember(tennisPlayer1);

            Debug.WriteLine(string.Join(" ", $"{club.ClubName} has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));

您应该阅读的其他参考资料是:
.


笔记;尽管在此示例中我没有涉及抽象,但是我确实将其与您已经构造代码的方式保持相对接近。但是,您本可以使用抽象数据层,但仍然可以达到相同的目标。这种方法与上面的示例完全不同,但仍然值得您继续学习。
 

跳伞者

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
从第一篇文章中我的理解是,他想为这种关系建模:
Screenshot_5.png


TennisPlayer有0或1个俱乐部。
俱乐部有0个或更多的TennisPlayer。

换句话说,TennisPlayer只能属于一个俱乐部,一个俱乐部可以有多个TennisPlayers。此外,TennisPlayer知道他属于哪个俱乐部,并且该俱乐部知道哪个TennisPlayers是成员。


实施#15结束时看起来像这样:
Screenshot_6.png

WithMembersOfClubs具有0个或更多的元组<TennisPlayer, Club>.
元组<TennisPlayer, Club>有1个TennisPlayer和1个俱乐部。

网球运动员现在不知道他/她属于哪个俱乐部,并且该俱乐部也不知道哪个TennisPlayers是其成员。现在,无论谁编写代码,都必须查阅第三个对象:WithMembersOfClubs对象以查找成员资格信息。此外,除非将额外的代码添加到WithMembersOfClubs中,否则将缺少TennisPlayer最多可以属于一个Club的强制实施。

这使我很难过,原因有几个。首先,它突然变得更加复杂。我们从两节课开始,现在我们最多有四节课。其次,我们现在要立即为关系数据库设计,而不是首先开始面向对象。上面的第一个UML图显示了一个很好的纯面向对象的视图,但是第二个图是我们所生活的当前世界的一种让步,在这个世界中,大多数人最终会将其对象持久化到关系数据库中。如果人们使用真实的面向对象的数据库,或者拥有非常聪明的ORM(对象到关系映射器),那么不需要Tuple记录TennisPlayer和Club之间的关系,(面向对象的)数据库将搞定此事。

有人可能会争辩说,第一个UML图是过于简单化。它隐藏了Club内部使用的TennisPlayer的列表。好的,这是一个不太简化的视图,显示了该细节级别:
Screenshot_7.png

但是对鹅有好处的对鹅也有好处,所以让我们还显示WithMembersOfClubs中的元组列表:
Screenshot_8.png


无论如何,这就是我处理TennisPlayer和Club的方式:
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

class TennnisPlayer
{
    public string Name { get; set; }

    Club _club;
    public Club Club
    {
        get => _club;

        set
        {
            if (value != _club)
            {
                var oldClub = _club;
                _club = null;
                oldClub?.RemoveMember(this);
                _club = value;
                _club?.AddMember(this);
            }
        }
    }

    public TennnisPlayer(string name, Club club = null)
    {
        Name = name;
        Club = club;
    }

    public override string ToString()
        => $"{Name} ({Club?.Name})";
}

class Club
{
    public string Name { get; set; }
    
    List<TennnisPlayer> _members = new List<TennnisPlayer>();
    public IEnumerable<TennnisPlayer> Members { get => _members; }

    public Club(string name)
    {
        Name = name;
    }

    public void AddMember(TennnisPlayer player)
    {
        if (!_members.Contains(player))
        {
            _members.Add(player);
            player.Club = this;
        }
    }

    public void RemoveMember(TennnisPlayer player)
    {
        if (_members.Contains(player))
        {
            _members.Remove(player);
            player.Club = null;
        }
    }

    public override string ToString()
        => $"{Name}'s Members: " + string.Join(",", Members);
}

class Program
{
    static void Main()
    {
        var clubA = new Club("A");
        var clubB = new Club("B");
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        var bjorn = new TennnisPlayer("Bjorn Borg");
        bjorn.Club = clubA;
        Console.WriteLine(bjorn);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        bjorn.Club = clubB;
        Console.WriteLine(bjorn);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        bjorn.Club = null;
        Console.WriteLine(bjorn);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        var john = new TennnisPlayer("John McEnroe", clubA);
        Console.WriteLine(john);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        john.Club = clubB;
        Console.WriteLine(john);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);

        john.Club = null;
        Console.WriteLine(john);
        Console.WriteLine(clubA);
        Console.WriteLine(clubB);
    }
}
 

羊皮

知名会员
已加入
2018年9月5日
留言内容
1,982
编程经验
10+
我觉得很遗憾您经历了所有这些解释,因为我不同意关于如何或应该如何做的一切。您在上面提出的要点;我不同意,并且我真的不在乎与您进一步讨论。当我花了足够的时间讨论这些问题时。

每个人都有自己的代码编写方式,我们中的一些人喜欢遵循某些原则而不是其他原则,这也很好。因此,与其接受挑剔,而不是尝试对其进行大刀阔斧的考验,只需接受您有编写自己的应用程序的理由(如您所展示的那样),并且知道我有做到这一点的方法。您在董事会之外进行的讨论已经讨论了这些要点。

说每个元组之间的关系破裂是荒谬的,也是错误的,网球运动员和俱乐部之间的关系也没有用关联来表示。因为他们显然是。正如您巧妙演示的那样,它们存储在列表中。列表中的每个条目都包含一个元组<tennisplayer, club>就像我在我提供的代码中从俱乐部中删除成员时所做的那样,可以使用简单的Linq语法来防止玩家两次成为成员或任何俱乐部。只需很少的检查,就可以防止用户被允许进入列表。

我坚持我的推理,没有理由说每个对象都应该保存任何数据-无论是网球运动员名单还是俱乐部名单。他们不需要彼此了解任何事情,如果您也确实想要他们,则应该使用继承。您目前提出的建议也挫败了给班级一个单一目标的目的。最后,如果您仔细看一下我的示例,它将得到一个对象,而不是四个对象。 WithMembersOfClubs 是您在程序的整个生命周期内或需要使用其中定义的List的生命周期内传递程序的唯一对象。我设想,无论使用我提供的示例的人,在函数中创建实例化对象都具有常识,在该实例中,他们将向俱乐部或网球运动员传递详细信息以使函数返回任一对象。

无论哪种方式,正如我已经对这些要点进行的论断一样,这将使我对这个话题充满精力。毕竟,它们都是面向对象的快乐编程,它们都是简单地以不同的方式面向对象的。
 

羊皮

知名会员
已加入
2018年9月5日
留言内容
1,982
编程经验
10+
@马修(Matthieu) -跳至最后一个代码块,以查看代码的主要源代码块,并保存阅读该代码的工作方式和更改的内容。

这是我以前的版本的改进。我关闭了元组(不是因为使用它们有什么问题)-没有,而是因为在下面的示例中我没有使用列表。我已经切换类型了。我以前使用列表的唯一原因是因为OP想要使用列表,并且元组适合该示例。我也可以用一个 SortedList 它允许您添加一个KeyValuePair。无论如何,我将该列表切换为的ConcurrentDictionary<TennisPlayer, Club>。 ConcurrentDictionary允许您从任何线程与它们进行交互,并且它们是线程安全的字典。我主要选择这本词典而不是标准词典,因为它允许您TryAdd / TryRemove 这基本上可以防止添加重复的条目,即。在增加用户两次或防止他们与其他俱乐部分开的情况下:
C#:
            if (membersOfClubs.AddTennisPlayerAsMember(tennisPlayer1, club))
                Debug.WriteLine(string.Join(" ", tennisPlayer1.PlayerName, "joined", club.ClubName));
            if (membersOfClubs.AddTennisPlayerAsMember(tennisPlayer1, club))
                Debug.WriteLine(string.Join(" ", tennisPlayer1.PlayerName, "joined", club.ClubName));
T的额外便利ryAdd / TryRemove 是它们在执行时返回布尔值,这使检查是否成功进入了集合变得很方便。这也解决了允许成员成为一个以上俱乐部的成员的问题(而前面的示例并未阻止这种情况,除非您进行了支票检查)。如前所述;那 尝试添加 如果尚未添加新的网球运动员,则只会添加一个新的网球运动员,并且调用此方法将返回bool值。

我还整理了Linq表达式,该表达式对给定俱乐部中的成员进行计数。我通过将where子句与count结合在一起来完成此操作。另外,我添加了一个新功能,以返回给定俱乐部的网球运动员的成员姓名列表。您可以看一下下面的示例,该示例仍然消除了网球运动员班级或俱乐部班级彼此了解任何知识的需要。我之所以这样写,是因为两个类都只需要根据OP创建一个或两个值即可。俱乐部名称,俱乐部地址等。以前,我使用构造函数代替这些函数来创建新的球员或俱乐部,并将它们返回到调用代码。从那以后,我将该代码更改为以下代码:
C#:
        public TennisPlayer InitializeNewPlayer(string playerName, string playerAddress, string playerPhoneNum) => new TennisPlayer
        {
            PlayerName = playerName,
            PlayerAddress = playerAddress,
            PlayerPhoneNumber = playerPhoneNum
        };
        public Club InitializeNewClub(string clubName, string clubAddress, string clubPhoneNum) => new Club
        {
            ClubName = clubName,
            ClubAddress = clubAddress,
            ClubPhoneNumber = clubPhoneNum
        };
返回类型后,您可以尝试将该新播放器添加到您的收藏中。此收藏的另一个好处是,您还可以使用 ICollection 和其他用于迭代集合的接口(我没有实现)。我更喜欢在设计这些类时使用这些类的另一个原因是,因为俱乐部和网球运动员都拥有一个 单一目的,目的是为每个新球员/俱乐部细节提供新参考。这给我们留下了一个单一的类对象,它将容纳所有网球运动员及其代表的俱乐部。该课程是: WithMembersOfClubs。这是完整的示例:

主代码块:
C#:
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace WPFDataBinding
{
    public partial class Window1
    {
        public Window1()
        {
            InitializeComponent();
            WithMembersOfClubs membersOfClubs = new WithMembersOfClubs();

            /* Create your first club */
            Club club = membersOfClubs.InitializeNewClub("Bondi Club", "33 Bondibeach Park", "0123456789");
            /* Create your first player */
            TennisPlayer tennisPlayer1 = membersOfClubs.InitializeNewPlayer("Sheepings", "Sheep ville", "0234567890");

            /* If member does not exist, write that he joined the club */
            if (membersOfClubs.AddTennisPlayerAsMember(tennisPlayer1, club))
                Debug.WriteLine(string.Join(" ", tennisPlayer1.PlayerName, "joined", club.ClubName));

            /* Create your second player */
            TennisPlayer tennisPlayer2 = membersOfClubs.InitializeNewPlayer("Bob", "Bob ville", "3214567890");

            /* If member does not exist, write that he joined the club */
            if (membersOfClubs.AddTennisPlayerAsMember(tennisPlayer2, club))
                Debug.WriteLine(string.Join(" ", tennisPlayer2.PlayerName, "joined", club.ClubName));

            /* Lets display the number of members in a given club */
            Debug.WriteLine(string.Join(" ", club.ClubName, "has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));

            /* Print the names of the players of a given club from the returned list */
            membersOfClubs.ListOfPlayerNames_By("Bondi Club").ForEach((string name) => Debug.WriteLine($"Member : {name}"));

            /* Remove the member from the given club if they exist */
            if (membersOfClubs.RemoveTennisPlayerAsMember(tennisPlayer1))
                Debug.WriteLine(string.Join(" ", tennisPlayer1.PlayerName, "left", club.ClubName));

            /* How many members remain after removing one? */
            Debug.WriteLine(string.Join(" ", club.ClubName, "has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));
        }
    }
    public class Club
    {
        public string ClubName { get; set; }
        public string ClubAddress { get; set; }
        public string ClubPhoneNumber { get; set; }
    }
    public class TennisPlayer
    {
        public string PlayerName { get; set; }
        public string PlayerAddress { get; set; }
        public string PlayerPhoneNumber { get; set; }
    }
    /// <summary>
    /// Here you can use an additional class to work with the objects of your other classes; such as Club, and TenniPlayer.
    /// This class can add your tennis players to a ConcurrentDictionary<TennisPlayer, Club>. When this class is first instantiated,
    /// keep a copy of the instantiated object to pass around your application. New instances of this class will create a new instance of _PlayerClubPair.
    /// </summary>
    public class WithMembersOfClubs
    {
        /// <summary>
        /// This Concurrent Dictionary keeps track of your tenis players and the clubs they are in.
        /// </summary>
        private readonly ConcurrentDictionary<TennisPlayer, Club> _PlayerClubPair = new ConcurrentDictionary<TennisPlayer, Club>();
        /// <summary>
        /// The TennisPlayer is initialised with a new instance of a TennisPlayer object and its details are provided upon instantiation.
        /// </summary>
        /// <param name="playerName">The name of your tennis player</param>
        /// <param name="playerAddress">The address of your tennis player</param>
        /// <param name="playerPhoneNum">The phone number of your tennis player</param>
        /// <returns>Returns the TennisPlayer you created</returns>
        public TennisPlayer InitializeNewPlayer(string playerName, string playerAddress, string playerPhoneNum) => new TennisPlayer
        {
            PlayerName = playerName,
            PlayerAddress = playerAddress,
            PlayerPhoneNumber = playerPhoneNum
        };
        /// <summary>
        /// The Club class is initialised with a new instance of a club object and its details are provided upon instantiation.
        /// </summary>
        /// <param name="clubName">The name of the club is provided here</param>
        /// <param name="clubAddress">The club address is provided here</param>
        /// <param name="clubPhoneNum">The clubs phone number is provided here</param>
        /// <returns>Returns the Club you created</returns>
        public Club InitializeNewClub(string clubName, string clubAddress, string clubPhoneNum) => new Club
        {
            ClubName = clubName,
            ClubAddress = clubAddress,
            ClubPhoneNumber = clubPhoneNum
        };
        /// <summary>
        /// This method will add players to your _PlayerClubPair collection and store the values of your tennis player and club in a key value pair <TennisPlayer, Club>.
        /// </summary>
        /// <param name="tennisPlayer">This is where you pass in the tennis player object</param>
        /// <param name="clubName">This is where you pass in the club object</param>
        public bool AddTennisPlayerAsMember(TennisPlayer tennisPlayer, Club clubName) => _PlayerClubPair.TryAdd(tennisPlayer, clubName);
        /// <summary>
        /// This method will remove all references of a TennisPlayer object passed into it.
        /// </summary>
        /// <param name="tennisPlayer">This is where you pass in the tennis player you wish to remove</param>
        public bool RemoveTennisPlayerAsMember(TennisPlayer tennisPlayer) => _PlayerClubPair.TryRemove(tennisPlayer, out _);
        public List<string> ListOfPlayerNames_By(string clubName) => (_PlayerClubPair.Where(pair => pair.Value.ClubName == clubName).Select(pair => pair.Key.PlayerName)).ToList();
        /// <summary>
        /// This method allows you to check the number of members in a given club object.
        /// </summary>
        /// <param name="clubName">The name of the club you want to check the number of players in</param>
        /// <param name="number">The number variable will be used to count how many members are in a given club</param>
        /// <returns></returns>
        public int NumberOfMembers_In(string clubName, int number)
        {
            number += _PlayerClubPair.Values.Count(club => club.ClubName == clubName);
            return number;
        }
    }
}

输出 :
C#:
羊皮 joined Bondi Club
Bob joined Bondi Club

Bondi Club has 2 Members

Member : Bob
Member : Sheepings

Sheepings left Bondi Club

Bondi Club has 1 Members

整理起来...
我这样做的方式主要是偏好问题,并在可能的情况下坚持让类具有单一目的。我将始终提倡创建基于上下文信息的类,例如要在上下文中保留的名称和地址,以便它们保留其单一用途。这不会使其他人提供的上述建议无效。也许他们不在乎以我的方式编写代码。这也很好。我们每个人都有自己喜欢遵循和打破的原则和规则,我敢肯定我们都有自己喜欢打破的规则,只要这样做真的很方便。有时我们需要同意不同意,因为当某项功能正常工作时,通常没有正确或错误的方式来执行某项操作。
 
最佳 底部