C# 向上兑换(虚构的)货币

C# 向上兑换(虚构的)货币,c#,C#,在一个使用古老的铜、银、金和白金货币体系的游戏中,每个面额的100个单位等于下一个最高面额的1个单位,这是一种可以接受的输入值“排序”或“向上改变”的方法吗 public struct Coinage { private int _copper; private int _silver; private int _gold; private int _platinum; public int Copper { get { ret

在一个使用古老的铜、银、金和白金货币体系的游戏中,每个面额的100个单位等于下一个最高面额的1个单位,这是一种可以接受的输入值“排序”或“向上改变”的方法吗


public struct Coinage
{
    private int _copper;
    private int _silver;
    private int _gold;
    private int _platinum;

    public int Copper
    {
        get { return _copper; }
        set
        {
            int val = value;
            while (val > 99) { val -= 100; Silver ++; }
            _copper += val;
        }
    }
    public int Silver 
    { 
        get { return _silver; } 
        set 
        {
            int val = value;
            while (val > 99) { val -= 100; Gold ++; }
            _silver += val;
        } 
    }
    public int Gold
    {
        get { return _gold; }
        set
        {
            int val = value;
            while (val > 99) { val -= 100; Platinum ++; }
            _gold += val;
        }
    }
    public int Platinum { get { return _platinum; } set { _platinum = value; } }
}
因此,无论我输入多少面值(低于铂金),它都能正确地为我兑换货币?像这样将属性的set方法链接起来是个坏主意吗?有没有一种更有效的方法可以用单一的方法来实现这一点


谢谢。

不,那太糟糕了。你不需要循环,你只需要除法和模

我也同意克里斯托弗的观点;只需将所有内容存储为最小的货币,并以您喜欢的方式显示即可。容易多了


编辑:哦,看看这个;我上面的代码[删除]中有一个严重的错误,看看你是否能发现它。更多的证据表明,您应该将所有内容存储为最小的货币,这就容易多了。

您的代码违反了最小惊喜原则。属性不应更改另一个属性的值

您应该有一个Add方法,该方法为每个面额接受一个参数,然后执行每个检查

如前所述,您不需要循环

silver += copper / 100;
copper = copper % 100;
gold += silver / 100;
silver = silver % 100;
//etc..

最简单也是最常用的方法就是将其存储为一个大整数(铜币数),然后使用模和除法提取每个“字段”。这是魔兽世界(World of Warcraft)等游戏所使用的(这就是为什么您在该游戏中的黄金上限为214748g 36s 47c-2147483647是您可以存储在32位整数中的最高数字)

例如,假设您有12345铜。这相当于1g,23s,45c(我们现在将忽略铂,因为这是相同的原理)。您可以按如下方式获取每个字段:

gold = money / 10000; //integer division
silver = (money % 10000) / 100; //first remove the part that was used for the gold, then do the division
copper = money % 100

鉴于您达到白金级别(每白金100万铜),在这种情况下选择64位整数(
long
)可能是个好主意。

首先,您的代码不起作用。如果我将白银设定为1000000,它将不起作用

造币的方式非常容易用数学计算。直到最后一分钟,才忘记它们之间的差异

public struct Coinage
{
    private int _val;

    public int Copper
    {
        get { return _val % 100; }
        set { _val += value }
    }
    public int Silver 
    { 
        get { return (_val % 10000) / 100; } 
        set { _val += value * 100; }
    }
    public int Gold
    {
        get { return (_val % 1000000) / 10000; }
        set { _val += value * 10000; }
    }
    public int Platinum 
    {
        get { return (_val % 100000000) / 1000000; }
        set { _val += value * 1000000; }
    }
}

好的-所以我评论说我会将其存储为一个值,并按照您的需要显示。下面是一个快速而肮脏的实现,让大家了解这个想法。我并没有费心检查负面或优化-只是想让大家了解这个想法

public class Coinage
    {
        public long Copper { get; set; }

        public override string ToString()
        {
            long change = Copper;

            var denominations = new[] {"Gold", "Silver"};

            int numberOfDenominations = denominations.Count();

            var result = new StringBuilder();

            foreach (var denomination in denominations)
            {
                int coppersToCurrentDenomination = ((int) Math.Pow(100, numberOfDenominations));

                long currentAmount = change / coppersToCurrentDenomination;
                result.AppendFormat("{0}:{1}", denomination, currentAmount);
                change -= (currentAmount * coppersToCurrentDenomination);

                numberOfDenominations--;
            }

            result.AppendFormat("Copper:{0}", change);

            return result.ToString();
        }
    }

我还建议您将
struct
设置为不可变
DateTime
是我如何设计它的一个很好的例子

public Coinage AddCopper(int amount)
{
    return new Coinage(_value + amount);
}

public Coinage AddSilver(int amount)
{
    return new Coinage(_value + (amount * 100));
}

放弃存储最小货币的想法,并在显示时向上计算:

我自己也试过了,下面是我的想法。如果我更加努力,我会创建一个名为“Currency”的单独类,该类包含一个名称和一个铜币值,并使用货币集合,而不是使用字符串数组和铜币值数组。此外,您可以将AddCurrency(int value)方法放在该类中一次,而不是针对每种不同类型的货币将其写出

如果你打算使用更多的货币,我建议你这样做。唯一的问题是确保所有货币都从最有价值到最不有价值

public class Coinage
{
    // Total number of liquidated copper coins 
    private int _value = 0;

    // Conversion ratios for each currency type
    private const int PLATINUM_VALUE = 1000;
    private const int GOLD_VALUE = 100;
    private const int SILVER_VALUE = 10;
    private const int COPPER_VALUE = 1;

    // Array of other denominations
    private string[] _currencyNames = { "Platinum", "Gold", "Silver", "Copper" };
    private int[] _currencyValues = { PLATINUM_VALUE, GOLD_VALUE, SILVER_VALUE, COPPER_VALUE };

    // Constructor
    public Coinage(int value)
    {
        _value = value;
    }

    public override string ToString()
    {
        string output = "";
        int value = _value;

        for (int i = 0; i < _currencyValues.Length; i++)
        {
            output += string.Format("{0}: " + (value / _currencyValues[i]) + "\n", _currencyNames[i]);
            value = value % _currencyValues[i];
        }
        return output;
    }

    public void AddCopper(int copper)
    {
        _value += copper;
    }

    public void AddSilver(int silver)
    {
        _value += silver * 10;
    }

    public void AddGold(int gold)
    {
        _value += gold * 100;
    }

    public void AddPlatinum(int platinum)
    {
        _value += platinum * 1000;
    }
}
公共等级硬币
{
//已清算的铜币总数
私有int_值=0;
//每种货币类型的兑换率
私有常数int白金值=1000;
私人建筑的黄金价值=100;
私人建筑单位银价=10;
私有const int铜_值=1;
//其他面额的数组
私有字符串[]_currencyNames={“白金”、“金”、“银”、“铜”};
私有int[]_currencyValues={白金值、黄金值、白银值、铜值};
//建造师
公共货币(整数值)
{
_价值=价值;
}
公共重写字符串ToString()
{
字符串输出=”;
int值=_值;
对于(int i=0;i<\u currencyValues.Length;i++)
{
output+=string.Format(“{0}:”+(value/_currencyValues[i])+“\n”、_currencyNames[i]);
value=value%\u currencyValues[i];
}
返回输出;
}
公共铜(内部铜)
{
_价值+=铜;
}
公共银牌(国际银牌)
{
_价值+=银*10;
}
公共无效添加黄金(整数黄金)
{
_价值+=黄金*100;
}
公共白金(国际白金)
{
_价值+=白金*1000;
}
}

当然,我应该测试输入是否为正,但为了简洁起见,我将保持原样。为什么还要这样存储它?现在,你也必须面对改变。为什么不将其全部存储为最低公分母(铜)并根据需要显示。我会将setter限制在[0,100)独占。您的代码不能很好地处理溢出,处理溢出只会使事情变得复杂。相反,我会创建一个helper函数来获取任意数量的溢出,然后将其分发。Superstringcheese:是的,有时是百分之一百的便士。但肯定是一个特定大小的整数。@Superstringcheese:您是说eLVE是不存在的?!?B-B-B-但是…*世界正在分崩离析*嗯,一个缺陷是它允许0除法。另一个缺陷是它让我看起来像个该死的业余爱好者,但我会让它溜走。它允许0除法在哪里?嗯,这有点苛刻。每个人都从某个地方开始。我个人建议对那些bil使用
long
lionaires在那里。我会在mod操作之前进行除法操作,这会让你更清楚地知道你在做什么。我会支持Chaos的建议,在这里使用long。使用int,它将溢出2000多个白金币。我仍然怀疑我可能想在代码的其他地方处理货币(def