using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Program
{
static int GetIntNumber(int min, int max, string prompt, string inputPrompt)
{
int number;
Console.Write(prompt);
do //loops as long as input is not between min and max
{
while (!int.TryParse(Console.ReadLine(), out number)) //loops as long as the input is not an integer
{
Console.Write("\nYou must enter an integer number!: ");
}
if (number < min || number > max) Console.Write(inputPrompt); //<--| write input prompt if number out of range
} while (number < min || number > max);
return number;
}
static void printGrade(string grade)
{
Console.WriteLine("\nYour potato is grade " + grade);
}
static void Main()
{
do
{
int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");
if (weight < 201)
{
printGrade("X");
}
else
{
if (weight < 401)
{
printGrade("A");
}
else
{
if (weight < 801)
{
printGrade("B");
}
else
printGrade("Z");
}
}
Console.WriteLine("\nPress any key to check the grade of another potato.");
} while (Console.ReadKey(true).Key != ConsoleKey.Escape );
Console.Read();
}
}
#1 楼
您非常正确地认为switch
语句不能采用这样的布尔运算符:switch
语句用于构建跳转表,并且只能使用常量来这样做。但是,您可以使用Dictionary
和一些LINQ轻松地为您的问题提供一个相当可靠的解决方案。由于您为问题初学者添加了标签,因此我会很友善地进行解释。
我们要做的第一件事是制作一个
Dictionary<int, string>
,它表示每个年级的最低值。var gradeMap = new Dictionary<int, string>
{
[0] = "X",
[201] = "A",
[401] = "B",
[801] = "Z",
};
因此,这意味着如果该值是
>= 801
,然后是Z
,依此类推。接下来,我们将在LINQ中将整个操作简单地单行处理:
gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= testWeight).Value
这是怎么回事? LINQ是语言集成查询,如果您曾经使用过SQL,则可能会认识到Query位,从本质上讲,LINQ允许您构建外观和行为类似于对
IEnumerable
对象(Dictionary
是对象)的查询的代码。因此,如果逐步进行操作,我们可以从以下字典开始:
0: X
201: A
401: B
801: Z
gradeMap.OrderByDescending(x => x.Key)
位将首先占用整个gradeMap
字典,然后按键值从高到低的顺序对其进行排序:801: Z
401: B
201: A
0: X
接下来,我们执行
FirstOrDefault(x => x.Key <= testWeight)
,它将循环遍历所有元素(在字典中的倒序排列),然后找到与我们的表达式匹配的第一个(x.Key <= testWeight
)。 x.Key
是字典键(我们的int
值)的引用:801: Z (Skipped)
401: B (Skipped)
201: A (Matched)
所以,现在我们有了字典中的
201: A
项,最后一部分是获得.Value
并返回其中的string
部分:A
中提琴,我们有我们的价值。 :)
我们可以将其组合在一起并得到:
var testWeight = 205;
var grade = gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= testWeight).Value;
所以,我们要做的最后一件事就是组合与您原来的程序一起使用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Program
{
static int GetIntNumber(int min, int max, string prompt, string inputPrompt)
{
int number;
Console.Write(prompt);
do //loops as long as input is not between min and max
{
while (!int.TryParse(Console.ReadLine(), out number)) //loops as long as the input is not an integer
{
Console.Write("\nYou must enter an integer number!: ");
}
if (number < min || number > max) Console.Write(inputPrompt); //<--| write input prompt if number out of range
} while (number < min || number > max);
return number;
}
static void printGrade(string grade)
{
Console.WriteLine("\nYour potato is grade " + grade);
}
static void Main()
{
do
{
var gradeMap = new Dictionary<int, string>
{
[0] = "X",
[201] = "A",
[401] = "B",
[801] = "Z",
};
int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");
printGrade(gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= weight).Value);
Console.WriteLine("\nPress any key to check the grade of another potato.");
} while (Console.ReadKey(true).Key != ConsoleKey.Escape );
Console.Read();
}
}
下一个最好的方法是将LINQ逻辑提取到新方法中:
public string GetGrade(int weight)
{
var gradeMap = new Dictionary<int, string>
{
[0] = "X",
[201] = "A",
[401] = "B",
[801] = "Z",
};
return gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= weight).Value;
}
然后我们的
Main
方法变为:static void Main()
{
do
{
int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");
printGrade(GetGrade(weight));
Console.WriteLine("\nPress any key to check the grade of another potato.");
} while (Console.ReadKey(true).Key != ConsoleKey.Escape );
Console.Read();
}
现在,无论出于何种原因,如果您需要添加新成绩,这都是微不足道的。
另外:如果您是未使用C#6.0的情况下,您必须将
var gradeMap
替换为以下内容:var gradeMap = new Dictionary<int, string>
{
{0, "X"},
{201, "A"},
{401, "B"},
{801, "Z"}
};
到目前为止,一切都很好,希望您能继续改进并了解有关C#和范例的更多信息它拥有。 :)
t3chb0t的回答也非常好,我建议也仔细阅读(尤其是有关方法重载的内容)。
评论
\ $ \ begingroup \ $
尽管我对此没有技术上的异议,但这只是一个刚起步的人,我真的不认为这是适合他们情况的合适答案。
\ $ \ endgroup \ $
–Loren Pechtel
16年11月7日在1:57
\ $ \ begingroup \ $
@LorenPechtel虽然这可能不是特定用户的最佳答案,但我们仍应努力让那些入门的用户(至少)了解他们将来可能会遇到的高级工具,以使他们以了解话题探索的方向。如果有解决问题的更好方法(这是尝试解决的问题),那么至少应将其提供给将来的读者。
\ $ \ endgroup \ $
– Der Kommissar
16年11月7日2:00
\ $ \ begingroup \ $
嘿,这很有趣,感谢您的答复。我或多或少了解它是如何工作的。作为初学者,我面临的挑战当然是要记住的语法数量...但是将来我会尽量记住这一点。
\ $ \ endgroup \ $
– S. Elliot Perez
16年11月7日在7:50
\ $ \ begingroup \ $
@ S.ElliotPerez每个人都从一个初学者开始,诀窍是不要失去兴趣。希望您能顺利成为一名强大的C#程序员。 :)
\ $ \ endgroup \ $
– Der Kommissar
16年11月7日在16:47
#2 楼
您可以将else if
放在一起。我怀疑这会更快吗,但对我来说它更干净,我喜欢偶数
if (weight <= 200)
{
printGrade("X");
}
else if (weight <= 400)
{
printGrade("A");
}
else if (weight <= 800)
{
printGrade("B");
}
else
{
printGrade("Z");
}
#3 楼
有什么可以改进的地方?您可以在几个地方改进您的代码。让我们从
GetIntNumber
开始。对重复的条件使用辅助变量。
使用辅助变量赋予条件含义。最好写评论。
使用积极的条件,并在必要时将其取反。通常,它们更容易理解。
借助良好的辅助变量,您将不需要注释,因为现在变量名称已经说明了
while
的运行时间。改进后的代码如下所示:static int GetIntNumber(int min, int max, string prompt, string inputPrompt)
{
int number;
Console.Write(prompt);
bool inputInRange;
do
{
while (!int.TryParse(Console.ReadLine(), out number)) //loops as long as the input is not an integer
{
Console.Write("\nYou must enter an integer number!: ");
}
inputInRange = number >= min && number <= max;
if (!inputInRange)
{
Console.Write(inputPrompt);
}
} while (!inputInRange);
return number;
}
方法重载
现在,让我们对
Main
进行一些处理,当前包含重要的逻辑。您可以创建另一种
PrintGrade
方法,但是该方法将需要int
。它将封装打印级逻辑,并使用return
避免嵌套或丑陋。其他建议:
使用PascalCase作为方法。
使用常量而不是幻数。
示例:
static class WeightGradeMaxValue
{
public const int X = 200;
public const int A = 400;
public const int B = 800;
public const int Z = -1; // -1 might indicate no limit
}
static void PrintGrade(int weight)
{
if (weight <= WeightGradeMaxValue.X)
{
PrintGrade("X");
return;
}
if (weight <= WeightGradeMaxValue.A)
{
PrintGrade("A");
return;
}
if (weight <= WeightGradeMaxValue.B)
{
PrintGrade("B");
return;
}
PrintGrade("Z");
}
您的
if/else
现在很短:static void Main()
{
do
{
int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");
PrintGrade(weight);
Console.WriteLine("\nPress any key to check the grade of another potato.");
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Console.Read();
}
更高级的解决方案
即使有所有建议的改进,仍然还有更多的余地。
可以将符号封装为新类型:
class Grade
{
public Grade(string symbol, int minWeight)
{
Symbol = symbol;
MinWeight = minWeight;
}
public string Symbol { get; }
public int MinWeight { get; }
public bool Includes(int weight)
{
return weight > MinWeight;
}
}
现在,该新类型可以检查重量是否在其范围内。这将帮助您搜索特定的成绩。
通常很容易理解正逻辑,因此我将其翻转。我没有将最大权重设置为最小权重,而是使之可以使用所有等级,并且逻辑变得更加明显,因为您现在检查的是该等级的权重,而不是其他等级的权重。
现在非常容易创建新成绩,我们通过
Main
对数组进行降序预排序,这样您就不必每次都这样做。private static readonly Grade[] Grades = new Grade[]
{
new Grade("X", 0),
new Grade("A", 200),
new Grade("B", 400),
new Grade("Z", 800),
}
.OrderByDescending(x => x.MinWeight)
.ToArray();
现在,搜索成绩就像编写一个LINQ表达式一样简单-它也很容易阅读,因为逻辑封装在
MinWeight
方法中:static Grade GetGrade(int weight)
{
return Grades.FirstOrDefault(x => x.Includes(weight)) ?? Grades.Last();
}
Includes
(null-coalescing运算符)表示如果在未找到任何内容的情况下第一部分返回??
,则使用第二部分或更详细地讲:它返回左操作数,如果操作数不为空;
最后一步是交换旧的
PrintGrade(weight);
用于新产品
PrintGrade(GetGrade(weight).Symbol);
评论
\ $ \ begingroup \ $
谢谢您的答复。我对为什么PrintGrade方法(在第二行代码中)包含其自身的实例感到有些困惑。同样,按照定义,它以整数作为值,但是您给它一个字符串作为参数...
\ $ \ endgroup \ $
– S. Elliot Perez
16年11月7日在8:23
\ $ \ begingroup \ $
@ S.ElliotPerez称为方法重载。您可以使用相同名称的多个方法,只要它们的参数不同即可。
\ $ \ endgroup \ $
–t3chb0t
16年11月7日在8:24
#4 楼
一些建议:为什么要计算两次
(number < min || number > max)
如果提取要求的公式即
if,则可以避免if-else (weight-1)/ 200
== 0 =>'X'
== 1 =>'A'
== 2 =>'B'
== 3 =>'B'
其他=='Z'
因此,您可以拥有一个数组
grades[5] = {'X','A','B','B','Z'}
factor = (weight-1)/200;
if(factor>=4) { factor = 4;}
return grade[factor];
我知道它的有点不可理解,但它很简短:)
其他选择是
switch
例只是实现了一个更简单的解决方案。
-使用带有键代表的字典每个范围的上限和具有该范围值的值,即
valuesForRange[200] = 'X';
valuesForRange[400] = 'A';
valuesForRange[800] = 'B';
valuesForRange[1500] = 'Z';
遍历键(如数组),并在匹配的任何地方获取值。
注意:您将误用字典来充当不同数据类型的2D数组。
评论
\ $ \ begingroup \ $
嘿,我将按照长度/复杂度的顺序进行所有这些操作。您的解决方案非常好且紧凑,谢谢。加上一些评论,它会更容易理解...唯一的问题是,在这种情况下,如果范围值的间隔不均匀,使用起来会变得更加困难。例如,如果它是类似“ X” = 0-257 gm,“ A” = 258-420 gm,“ B” = 421-545 gm之类的东西,那么我可能不得不寻找其他解决方案。
\ $ \ endgroup \ $
– S. Elliot Perez
16年11月7日在7:31
\ $ \ begingroup \ $
“我知道它有点难以理解,但它很短”-您应该始终主张理解而不是简洁。您从代码“简短”和难以理解中获得了任何好处,除了想知道“这又会做什么?”几天后查看时。
\ $ \ endgroup \ $
– 404
16年7月7日在13:38
\ $ \ begingroup \ $
绝对不要为了简洁而妥协自己的解释性代码,我对此表示赞同。该解决方案适合于所放置的要求:) @Elliot:我添加了另一个解决方案以使其简洁。
\ $ \ endgroup \ $
– Thepace
16年11月7日在16:01
评论
我对C#一无所知,这就是为什么我只留下评论,但是该语言肯定支持三元运算符吗? printGrade(重量<201?“ X”:重量<401?“ A”:重量<801?“ B”:“ Z”)x <201是布尔变量表达式。它返回true或false或0或1。不能将其评估为一系列数字。因为0和1的集合中何时是3?