我最近开始“编码”,这是我的第一个“项目”。它应该是一个SI转换器,您可以在其中键入值,其单位以及要将其转换为的单位。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Program
{
    class Program
    {
        static void Main()
        {


            decimal one = 1;
            decimal two = 0.001m;
            decimal three = 0.000001m;
            decimal four = 0.000000001m;
            decimal five = 0.000000000001m;
            decimal answer;
            int y = 0;

            while (y < 1)
            {


                Console.WriteLine("SI converter!\nPlease, enter value: ");
                decimal value = Convert.ToInt32(Console.ReadLine());

                Console.WriteLine("\nFactors: \n1.One \n2.Milli(m)\n3.Micro(µ)\n4.Nano(n)\n5.Pico(p)\nEnter factor: ");
                decimal factor = int.Parse(Console.ReadLine());

                if (factor == 1)
                {
                    factor = one;
                }
                else if (factor == 2)
                {
                    factor = two;
                }
                else if (factor == 3)
                {
                    factor = three;
                }
                else if (factor == 4)
                {
                    factor = four;
                }
                else if (factor == 5)
                {
                    factor = five;
                }


                Console.WriteLine("\nFactors: \n1.One \n2.Milli(m)\n3.Micro(µ)\n4.Nano(n)\n5.Pico(p)\nEnter the second factor: ");
                decimal factor2 = Convert.ToInt32(Console.ReadLine());

                if (factor2 == 1)
                {
                    factor2 = one;
                    answer = value * factor;
                    Console.WriteLine("The answer is : " + answer);
                }
                else if (factor2 == 2)
                {
                    factor2 = two;
                }
                else if (factor2 == 3)
                {
                    factor2 = three;
                }
                else if (factor2 == 4)
                {
                    factor2 = four;
                }
                else if (factor2 == 5)
                {
                    factor2 = five;
                }



                answer = value * factor / factor2;
                Console.WriteLine("The answer is : " + answer);


                Console.WriteLine("Go again?\nY / N");
                char ans = char.Parse(Console.ReadLine());
                if (ans == 'n')
                {
                    y = 8;
                }
                if (ans == 'y')
                {

                    y = 0;
                    Console.Clear();

                }
            }
        }
    }
}
    }


可以给我吗?有关代码以及如何改进的一些意见?

评论

欢迎使用代码审查!我已回滚上一次编辑。收到答案后,请查看您可能会做什么和可能不会做什么。

顺便说一句,由于您对单位转换和.NET感兴趣,因此您可能还希望了解F#度量单位。

#1 楼

我将开始使您的因子命名与SI因子保持一致,例如

        decimal unit = 1;
        decimal milli = 0.001m;
        decimal micro = 0.000001m;
        decimal nano = 0.000000001m;
        decimal pico = 0.000000000001m;


还有更多类似

        decimal kilo = 1000.0;
        decimal mega = 1000000.0;
        decimal deci = 0.1m;
        decimal centi = 0.01m;


这样可以使您的代码意图在具有如下语句时更加可读和简洁:

if (factor == 1) {
    factor = unit;
}



我也将用户分开选择和选择的因素要使用不同的变量,例如:

            int choice = Convert.ToInt32(Console.ReadLine());

            switch(choice) {
            case 1:
                factor = unit;
                break;
            case 2:
                factor = milli;
                break;
            // etc.
            }


要在用户输入和选择的实际因素之间进行清楚的区分。这将使代码更加可读,可维护和简洁。


此外,由于您一直在专门要求c#,因此您可以直接将用户输入作为字符串输入,而无需使用从一个数字进行映射:

Console.WriteLine("\nFactors: \nUnit \nMilli(m)\nMicro(µ)\nNano(n)\nPico(p)\nEnter factor: ");
string choice = Console.ReadLine();

switch(choice) {
case "Unit":
    factor = unit;
    break;
case "Milli":
    factor = milli;
    break;
// etc.
}


,或者从正确初始化的Dictonary<string, double>中查找因子值。

评论


\ $ \ begingroup \ $
是的,我正在考虑进行其余的工作(Kilo,Mega,Giga,Tera),但我不知道我是否能做到:D
\ $ \ endgroup \ $
– BatmanB。
2015年12月2日,19:33

\ $ \ begingroup \ $
我尝试过,但是我不能将十进制与开关结合起来……或者至少我不知道如何。
\ $ \ endgroup \ $
– BatmanB。
2015年12月2日,19:34

\ $ \ begingroup \ $
开关就是您要询问用户的内容,以及想要转换成另一个单位的内容。因此,仅添加更多数字,并在内部解释这些映射到因子的情况。通常,保持该输入与输入给出的值不直接相关。
\ $ \ endgroup \ $
–πάνταῥεῖ
2015年12月2日,19:40

\ $ \ begingroup \ $
@BatmanB。我只是从简单的POV那里回答如何改进现有代码。当然,要准备生产,您应该使用Caridorc的通用解决方案,该解决方案完全避免了if / else或swich / case。
\ $ \ endgroup \ $
–πάνταῥεῖ
2015年12月2日于20:07



\ $ \ begingroup \ $
if(factor == 1){factor = unit; }似乎毫无用处。我不知道推广它是否那么好。除此之外,很棒的第一答案!欢迎!
\ $ \ endgroup \ $
–莫妮卡基金的诉讼
2015年12月3日,0:36

#2 楼

我建议从以下形式进行简化:

        decimal one = 1;
        decimal two = 0.001m;
        decimal three = 0.000001m;
        decimal four = 0.000000001m;
        decimal five = 0.000000000001m;
        decimal answer;
        int y = 0;

        while (y < 1)
        {


            Console.WriteLine("SI converter!\nPlease, enter value: ");
            decimal value = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("\nFactors: \n1.One \n2.Milli(m)\n3.Micro(µ)\n4.Nano(n)\n5.Pico(p)\nEnter factor: ");
            decimal factor = int.Parse(Console.ReadLine());

            if (factor == 1)
            {
                factor = one;
            }
            else if (factor == 2)
            {
                factor = two;
            }
            else if (factor == 3)
            {
                factor = three;
            }
            else if (factor == 4)
            {
                factor = four;
            }
            else if (factor == 5)
            {
                factor = five;
            }




int y = 0

while (y < 1)
{
    Console.WriteLine("SI converter!\nPlease, enter value: ");
    decimal value = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("\nFactors: \n1.One \n2.Milli(m)\n3.Micro(µ)\n4.Nano(n)\n5.Pico(p)\nEnter factor: ");
    decimal exponent = int.Parse(Console.ReadLine());

    factor = Math.pow( 10, ( - ( (exponent - 1) * 3 ) ) )


在对数刻度上均匀分布,其中每个系数是-10**3的前一个系数。

某些上下文

科学计数法中的数字\ $ x \ $的形式为:

$$ x = a * Pow(10,b)$$


其中\ $ a \ $是从\ $ 0 \ $到\的数字$ 9.9 \ $
\ $ b \ $是整数

现在您在程序中列出的各种前缀都比另一个小一个\ $ 1000 \ $。

这意味着要从一个前缀到下一个前缀,您必须将其乘以\ $ Pow(10,-3)\ $

,因此,给定n则将其乘以\ $ Pow(10,-3 * n)\ $其中\ $ n \ $是输入数字,您会得到\ $ n-th \ $后缀。

\ $-1 \ $只是从1索引到0索引。


简化形式是完全删除y变量并将其结尾修改为:

        if (ans == 'n')
        {
            return;
        }
        if (ans == 'y')
        {
            Console.Clear();
        }


立即返回也更容易理解,即将声明在几十行之前的计数器设置为8(出于好奇:为什么是8而不是1或2?)

评论


\ $ \ begingroup \ $
哇,这比我的还简单!虽然我不太明白:D
\ $ \ endgroup \ $
– BatmanB。
2015年12月2日,19:24

\ $ \ begingroup \ $
@BatmanB。它比看起来容易,您知道科学计数法吗?
\ $ \ endgroup \ $
– Caridorc
2015年12月2日,19:25

\ $ \ begingroup \ $
Kinda ...我们在做“转变”吗?在学校使用SI单位,所以我决定我可以尝试制作某种可以为我做的转换器:D
\ $ \ endgroup \ $
– BatmanB。
15年2月2日在19:29

\ $ \ begingroup \ $
@BatmanB。我添加了一些上下文,告诉我是否更清楚
\ $ \ endgroup \ $
– Caridorc
2015年12月2日,19:36

\ $ \ begingroup \ $
@Caridorc感谢您的解释,我几乎理解了...(关于8,我真的不知道为什么我这么做了)
\ $ \ endgroup \ $
– BatmanB。
2015年12月2日19:45



#3 楼

这将是一个更长的示例,在这里我只讨论转换逻辑,因为我认为这是最有启发性的。从您提供的特定案例不能证明如此接近如此结构的意义上说,它也不是“真实世界”的示例,但是您可以很容易地在生产规模的应用程序中朝这个方向前进。 >
我提供的代码可用于处理输入和所需的因子。

在此处查看转换和多个代码

要点
再次,这是一个“教学示例”。作为初学者,您不应期望您的代码看起来像这样。一次仅一步:)
相关数据属于同一类型。如果没有前缀名称,则复数是没有意义的。

类!

创建代表代表在一起的值集的类型通常是一个好主意。 SI倍数就是一个例子。名称“ nano”与其因子相关。因为它们全部构成一个复合实体或构想,所以它们在类型(structclass)中才有意义,这是我提供的代码中的Multiple类型。

public struct Multiple
{
    public Multiple(string name, int exponent)
    {
        Name = name;
        Exponent = exponent;
    }
    public string Name { get; }  // Read-Only Property
    public int Exponent { get; } // Read-Only Property
    public double Multipler => Math.Pow(10, Exponent);
    public double ConvertToBaseValue(double inputValue)
    {
        return inputValue/Multipler;
    }
    public double ConvertFromBaseValue(double inputValue)
    {
        return inputValue*Multipler;
    }
    public UnitValue CreateValue(double inputValue)
    {
        var newBaseValue = ConvertToBaseValue(inputValue);
        return new UnitValue(this, newBaseValue);
    }
    public static Multiple Singular => new Multiple(null, 0);
    public static Multiple Milli => new Multiple("Milli", -3);
    public static Multiple Micro => new Multiple("Micro", -6);
    public static Multiple Nano => new Multiple("Nano", -9);
    public static Multiple Pico => new Multiple("Pico", -12);
}


同样,您的输入有两个问题(值是多少?我想用什么单位显示?)构成UnitValue类型的基础。如果将两者彼此分开,它们的意义不大。

public class UnitValue
{
    public UnitValue(Multiple multiple, double baseValue)
    {
        Multiple = multiple;
        BaseValue = baseValue;
    }
    public Multiple Multiple { get; }
    public double PrefixedValue
    {
        get { return Multiple.ConvertFromBaseValue(BaseValue); }
        set { BaseValue = Multiple.ConvertToBaseValue(value); }
    }
    public double BaseValue { get; set; }
    public UnitValue ConvertTo(Multiple multiple)
    {
        return new UnitValue(multiple, BaseValue);
    }
}


默认值的静态/类方法

我已将工厂静态方法添加到Multiple来标准化值。您希望所有标准定义都内置在该类型中,或者要在附近使用。

    // Members of Multiple type.
    public static Multiple Singular => new Multiple(null, 0);
    public static Multiple Milli => new Multiple("Milli", -3);
    public static Multiple Micro => new Multiple("Micro", -6);
    public static Multiple Nano => new Multiple("Nano", -9);
    public static Multiple Pico => new Multiple("Pico", -12);
    // Allows you to get multiples like so.
    var m = Multiple.Milli;
    var u = Multiple.Micro;


不要重复自己!

每个人会一遍又一遍地说。在这里,我认为通过避免输入带有所有零的乏味值来证明这一点。它还避免了我的前缀为0.0001的错误。

    // Risky
    public static Multiple Pico => new Multiple("Pico", 0.000000000001);
    // Safer
    public static Multiple Pico => new Multiple("Pico", -12);


属性访问器

我没有决定让用户使用特殊的方法来更新基值或前缀值,而是决定将BaseValue属性设置为自动属性(这意味着我无需为该属性编写样板代码的gettor和setter),并连接了PrefixedValue。这样,您可以使类型用户的生活更轻松一些,但要以使他们有责任知道何时使用不同的值以及为什么使用它们为代价。

    public double PrefixedValue
    {
        get { return Multiple.ConvertFromBaseValue(BaseValue); }
        set { BaseValue = Multiple.ConvertToBaseValue(value); }
    }


仅获取属性

由于Multiple数据类型属于同一类,因此客户端对其进行更新没有任何意义。因此,为防止数据损坏,我使它们仅获得在实例化对象时设置的属性。

    // Constructor assigns the values when instantiated.
    public Multiple(string name, int exponent)
    {
        Name = name;
        Exponent = exponent;
    }
    public string Name { get; }  // Can only be set when instantiated.
    public int Exponent { get; } // Can only be set when instantiated.


摘要

这些是您前进时要考虑的一些事项。我已经跳过了其他需要理解的重要内容,但是它们可以等到您有更多的经验为止:


将程序编写到接口,而不是实现上。
使用继承进行专门化(如果我想处理英制重量)。
使用反射对所有预定义的倍数进行迭代,以便可以更改定义的倍数,并且UI会自动更新。
单元测试代码验证设计。

我希望那不是太吓人!如果有人有任何反馈,我希望能听到。

编辑1:添加用法示例

我在这里修改了要点,以包括Main程序的示例,如果它使用的是我编写的类。
除了删除重复项和将登录细节推送到Log方法之外,我没有在那里进行完整的重构练习。

为什么要替换Console.WriteLine()?这是平台功能

的确如此。但是您仍然在各处重复进行。想像一下您是否想更改记录方式?还是添加标准格式?

如果使实现远离日志功能的用户,则可以随时更改它。如果要用日志文件替换控制台日志记录功能,可以在一个位置进行更改。没有这种抽象,您实际上将更新数十个引用。

    // "Brittle" Code
    Console.WriteLine("Bob");  // In one file
    Console.WriteLine("Says"); // In another file
    Console.WriteLine("Hi!");  // In a third file

    // Abstracted Code
    Log("Bob");
    Log("Bob");

    private static void Log(string message)
    {
        Console.WriteLine(message);
    }


最后一件事情您还想考虑如果用户输入一些奇怪的内容会发生什么。不包含围绕用户输入的任何错误处理。

评论


\ $ \ begingroup \ $
在没有为初学者留下示例的情况下,有些过于不确定,以至于到目前为止所有示例都可能超出他们已经获得的概念。尽管我同意您提出的所有观点。
\ $ \ endgroup \ $
–πάνταῥεῖ
2015年12月2日于20:36

\ $ \ begingroup \ $
感谢@πάνταῥεῖ!我已经相应地修改了要点,并添加了Main.cs文件和所做的更改。
\ $ \ endgroup \ $
–富Seviora
2015年12月3日,0:21

\ $ \ begingroup \ $
@RubberDuck你绝对是对的:)现在已经完成了。
\ $ \ endgroup \ $
–富Seviora
2015年12月3日,下午1:59

\ $ \ begingroup \ $
我认为这是最好的答案。当存在较大的设计缺陷时,仅解决一些小问题是没有用的。特别是因为OP是初学者,那么他应该知道应该如何做……在CR上要问什么呢?
\ $ \ endgroup \ $
–阿德里亚诺·雷佩蒂(Adriano Repetti)
2015年12月3日在8:57



\ $ \ begingroup \ $
谢谢,但是我真的不能上课...
\ $ \ endgroup \ $
– BatmanB。
2015年12月3日,12:08