在 C# 中编写多个构造函数重载的最佳方法

Best way to write multiple constructor overloads in C#

正在学习C#,做了个简单的"Player"class。但我很难承受多重超载。 这是我最好的解决方案,但我觉得可以做到 simpler/better。

class Player : Entity
    {
        public Player() {
            Name = "Player";
            XP = 0;
            LVL = 1;
            XPToLvlUp = 10;
            XpRank = 10;
        }

        public Player(string name) : this() {
            Name = name;
        }

        public Player(string name, int _Hp, int _Mp) : this(name) {
            HP = _Hp;
            MP = _Mp;
        }

        public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl) : this(name, _Hp, _Mp) {
            XP = _Xp;
            LVL = _Lvl;
        }

        public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl, int XpByRank) : this(name, _Hp, _Mp, _Xp, _Lvl) {
            XpRank = XpByRank;
        }

        //deleted code for better reading

        private int XPToLvlUp;
        private int XpRank;
        public int XP;
        public int LVL;
        public string Name;
    }  

它好吗,如果不好请告诉我为什么。 感谢您的回复!

我觉得这样很好。有一个问题要问自己:这些方法中的每一个实际上都可能被调用吗?

一种选择是让程序员在实例化 class:

之后设置这些值
var myPlayer = new Player();
myPlayer.XP = 5;

但是,在某些情况下,您确实需要预先了解所有信息,因此这可能并不合适。

另一个选项可以是传递给 ctor 的选项 class:

public class PlayerSettings
{
  public Name = "Player";
  public XP = 0;
  public LVL = 1;
  public XPToLvlUp = 10;
  public XpRank = 10; 
}

那么你的 ctors 看起来像这样:

public Player() : this(new PlayerSettings())
{
}

public Player(PlayerSettings settings)
{
  //Fill in appropriate variables here
}

该选项将以这种方式调用:

var playerSettings = new PlayerSettings() { XP = 5 };
var myPlayer = new Player(playerSettings());

最后,我不确定一个比另一个"better",这在很大程度上取决于您的需求。

您的 class 几乎可以接受。

短篇小说: 使用属性。

长话短说:

首先制定或遵循命名规则,它会让你的代码更易于阅读。这取决于你,只是一个建议。对于由多个单词组成的复杂名称,您可以使用 CamelCasedNames。并避免缩短可能有用的所有类型数据的名称。例如,您可以将 Lvl 扩展到 Level,但 XpExperience 看起来很奇怪。这也取决于你。

string name; // local Variable, first character lower cased
private string _name; // private Field, first character is lower cased with leading "_"
public string Name { get; set; } // public Property, first character is upper cased

我将向您展示覆盖构造函数的替代方案,并将遵循命名规则。

1) 构造函数的默认值(使用 class 的一部分以保持简单)

class Player
{
    public Player(string name = "Player", int xp = 0, int level = 1)
    {
        Name = name;
        Xp = xp;
        Level = level;
    }

    // Properties instead of Fields
    public int Xp { get; private set; } // restrict modification of the property outside of a class but reading is available
    public int Level { get; private set; }
    public string Name { get; set; }
} 

2) 没有带默认值的构造函数的属性

第一个 属性 目的是限制对数据的访问以保持内部对象数据的一致性。即使你在代码中犯了错误。避免一些错误的好方法。

第二个 属性 目的是在获取或设置代码时执行代码。例如,使属性相互依赖以存储更少且仅存储唯一数据。

class Player
{
    public int Xp { get; private set; } = 0;
    public int Level { get; private set; } = 1;
    public string Name { get; set; } = "Player";
} 

用法

Player player = new Player() { Name = "KillerPWNZ", Level = 100, Xp = 999999 };

奖励: 另一个 属性 功能

您可以执行 getset 子句中的任何代码。

让我们假设下一个玩家的等级需要双倍的 xp,但第 2 级需要 100 XP。您决定向第 1 级玩家开具 1000 XP 的发票。显然,您需要按几次 Level。假设 Xp 包含相对于 Level 的值。

发票

player.Xp += 1000;

属性 带代码

private int _xp = 0;

public int Level { get; private set; } = 1;
public int Xp
{
    get => _xp; // same as: get { return _xp; }
    set
    {
        _xp = value; // here value is keyword containing data you want to set
        while (_xp >= GetXpPerLevel(Level))
        {
            _xp -= GetXpPerLevel(Level);
            Level++;
        }
        while (_xp < 0 && Level > 1)
        {
            _xp += GetXpPerLevel(Level - 1);
            Level--;
        }
    }
}

// helper method
private int GetXpPerLevel(int level)
{
    if (level < 1) return 0;

    // int result = 100;
    // for (int i = 1; i < level; i++) result *= 2;
    // return result;

    // or the same with some binary shift magic :)
    return 100 << (level - 1);
}