简体   繁体   English

C#:根据用户输入创建字符串数组,然后,如果无法将其解析为int数组,请重试输入

[英]C#: Creating a string array from user input, then if it can't be parsed into int array, retrying the input

I've been trying to make a matrix console application in C# but I am struggling when it comes to checking viability of user input. 我一直在尝试使用C#制作矩阵控制台应用程序,但是在检查用户输入的可行性时,我一直很努力。 Here's the code: 这是代码:

//INPUT OF VALUES INTO MATRIX A BY ROWS
int ValuesCounter = 2; //index for new arrays created on fail.
string[] valuesValuesCounter = Console.ReadLine().Split(' '); //read user input into string array
int[] valuescheckValuesCounter = new int[valuesValuesCounter.Length]; //test array to check if user input can be parsed into int
for (int i = 0; i <= valuesValuesCounter.Length; i++) // loop for checking parseability of individual array items
{
    while(!int.TryParse(valuesValuesCounter[i], out valuescheckValuesCounter[i])) //same
    {
        /*if it can't be parsed, create new array and try again*/
        ValuesCounter += 2;
        Console.WriteLine("Wrong values! Please try again!");
        valuesValuesCounter = Console.ReadLine().Split(' ');
        valuescheckValuesCounter = new int[valuesValuesCounter.Length];
    }
}

I doubt this is very efficient. 我怀疑这是非常有效的。 I've tried using ArrayList instead because you can just remove the values in it, but it can't be split so I don't know how to make it work. 我尝试使用ArrayList代替,因为您只能删除其中的值,但是无法拆分,因此我不知道如何使其工作。 What is a better way to do this? 有什么更好的方法可以做到这一点?
Also tried conducting my own research, but didn't find any solution (maybe it's there and I am just stupid for not seeing it, I don't know). 还尝试进行自己的研究,但没有找到任何解决方案(也许它在那里,我只是因为没有看到它而愚蠢,我不知道)。

How to delete an array in c#? 如何在C#中删除数组?
Convert 2D string array into 2D int array (Multidimensional Arrays) 将2D字符串数组转换为2D int数组(多维数组)
C# arraylist user input C#arraylist用户输入
int.TryParse out into an object array (this seems pretty close but I don't see anything there to retry the input in case it can't be parsed). int.TryParse解析为一个对象数组 (这看起来很接近,但是我看不到有任何东西可以重试输入以防无法解析)。

So there you have it. 所以你有它。 I'm pretty dumb so if this question seems stupid it's because the one asking it is stupid. 我很傻,所以如果这个问题看起来很愚蠢,那是因为问这个问题的那个愚蠢的人。

First of all, make the user experience as good as possible. 首先,使用户体验尽可能好。 Inputing a matrix all at once can be error prone, make the user's life easier and take it step by step. 一次输入矩阵容易出错,使用户的生活更加轻松并逐步进行。

Second, make small methods that perform simple tasks; 第二,制作执行简单任务的小型方法。 they are easier to reason about, easier to write, easier to debug and easier to modify if new requirements come along. 如果出现新的要求,它们就更容易推理,更容易编写,更容易调试和更容易修改。 Don't make methods do too much, it will only buy you headaches and more debugging hours than necessary. 不要让方法做太多事情,它只会给您带来头痛,并且会花费不必要的调试时间。 If you are learning, don't worry if it seems you are breaking down to seemingly absurdly simple tasks, you wont regret it. 如果您正在学习,请不要担心似乎正在分解为看似荒谬的简单任务,您不会后悔。 Always remember that the most complicated problems you can imagine are always solved solving smaller and easier ones. 永远记住,您能想象到的最复杂的问题总是可以解决较小更简单的问题。

So, the first thing you need is a method that prompts the user for a valid integer, so lets try that: 因此,您需要的第一件事是一种提示用户输入有效整数的方法,因此让我们尝试一下:

private static int GetIntegerFromUser(string prompt)
{
    int value;
    Console.Write($"{prompt}: ");

    while (!int.TryParse(Console.ReadLine(), out value))
    {
        Console.WriteLine("That is not a valid value. Please try again.");
        Console.Write($"{prompt}: ");
    }

    return value;
}

See how that works? 看看如何运作? This method has one single goal in life; 这种方法在生活中有一个目标。 get the user to enter a valid integer. 让用户输入有效的整数。 Never mind what the integer is for, it doesn't care, thats not its job, this method will keep asking the user until its goal in life is achieved. 不必介意整数是什么,它不在乎,那不是它的工作,这种方法将不断询问用户,直到实现其生活目标为止。

Ok, whats next? 好的,接下来是什么? When filling in a matrix we'll need to know: 填写矩阵时,我们需要知道:

  1. Number of rows 行数
  2. Number of columns 列数
  3. Values 价值观

Ok lets write some methods that fetch just that: 好吧,让我们写一些方法来获取:

private static (int Rows, int Columns) GetMatrixSize()
{
    var rows = GetIntegerFromUser("Please enter number of rows");
    var columns = GetIntegerFromUser("Please enter number of columns");
    return (rows, columns);
}

And, 和,

private static int[,] GetMatrixValues(int rows, int columns)
{
    var matrix = new int[rows, columns];

    for (var row = 0; row < rows; row++)
    {
        for (var column = 0; column < columns; column++)
        {
            matrix[row, column] =
               GetIntegerFromUser($"Enter matrix value [{row}, {column}]");
        }
    }
}

Now you should come to realize why making small methods that do simple tasks is such a great idea. 现在您应该意识到为什么制作执行简单任务的小型方法是一个好主意的原因。 Because chances are really good you'll get to reuse them and once you know the method works correctly, it will work correctly everywhere. 因为机会真的很好,所以您将可以重用它们,一旦您知道该方法正确运行,它便会在任何地方正确运行。

Ok, we've got all we need, now its just a matter of putting it all together: 好的,我们已经有了所有需要的东西,现在只需将它们放在一起即可:

public static int[,] GetMatrixFromUser()
{
    var size = GetMatrixSize();
    return GetMatrixValues(size.Rows, size.Columns);
}

How easy does that read? 读起来有多容易?

So, we're done! 至此,我们完成了! Really? 真? Well... no. 好吧 We have no value validation at all, and that can be a problem. 我们根本没有价值验证,这可能是一个问题。 What happens if someone decides to input a negative number of rows? 如果有人决定输入负数行会怎样? Ok, no problem, lets make a method that makes sure a value is inside a valid range: 好的,没问题,让我们建立一个确保值在有效范围内的方法:

private static bool IsInRange(int value, 
                              int lowerInclusiveBound,
                              int upperExclusiveBound,
                              string messageOnFailedValidation)
{
    if (value >= lowerInclusiveBound &&
        value < upperExclusiveBound)
        return true;

    Console.WriteLine(messageOnFailedValidation);
    return false;
}

And now, becuase we've used small methods that do well defined tasks, adding the new requirement is easily done modifying our GetMatrixSize method a little: 现在,由于我们使用了可以很好地完成定义任务的小型方法,因此添加新要求很容易就可以对GetMatrixSize方法进行一些修改:

private static (int Rows, int Columns) GetMatrixSize()
{
    int rows, columns;

    do
    {
        rows = GetIntegerFromUser("Please enter number of rows");
        columns = GetIntegerFromUser("Please enter number of columns");
    } while (!IsInRange(rows,
                        1,
                        int.MaxValue,
                        "Number of rows must be equal or greater than one.") |
             !IsInRange(columns,
                        1,
                        int.MaxValue,
                        "Number of columns must be equal or greater than one."));

    return (rows, columns);
}

Yeah, now we are done... 是的,现在我们完成了...

Almost! 几乎! One last thing. 最后一件事。 Another good habit is making sure the truths you believe in are actually true. 另一个好习惯是确保您所相信的事实是真实的。 What are you believing to be true inside GetMatrixValues ? 您认为GetMatrixValues内部的GetMatrixValues什么? You believe that rows and columns will have valid values. 您认为rowscolumns将具有有效值。 But your belief is purely based on faith, you are not checking if this is true. 但是您的信念纯粹基于信念,您没有检查这是否是真的。

But wait... I already checked in GetMatrixSize right? 但是等等...我已经签入GetMatrixSize吗? Yes, but what guarantee do you have that GetMatrixValues won't be called from some other method later on? 是的,但是您如何保证GetMatrixValues不会再通过其他方法调用GetMatrixValues Reusability remember? 可重用性还记得吗?

Here you can go two paths. 在这里,您可以走两条路。 You can throw an exception if the values are not valid or you can assert that the values are in fact valid. 如果值无效或可以断言这些值实际上有效,则可以引发异常。 Because the method is private and having invalid values probably means a bug somewhere up the callstack, the better option here is to assert your truths: 由于该方法是私有的,并且具有无效值,可能意味着在调用堆栈中的某个地方存在错误,因此,更好的选择是声明您的真相:

private static int[,] GetMatrixValues(int rows, int columns)
{
    Debug.Assert(rows > 0 && columns > 0);
    var matrix = ...
}

Any other truths merely on faith? 还有其他关于信仰的真理吗? Who is making sure that upperExclusiveBound and lowerInclusiveBound in IsInRange actually make sense? 谁是确保upperExclusiveBoundlowerInclusiveBoundIsInRange实际意义? Is someone stopping you from making the following call: IsInRange(10, 100, -100, ...) ? 是否有人阻止您拨打以下电话: IsInRange(10, 100, -100, ...) A call like that is probably a bug somewhere else in your code, make your life easier by making these kind of bugs easier to spot. 这样的调用可能是代码中其他地方的错误,通过使这类错误更容易发现,可以使您的生活更轻松。 Again, assert your truths: 再次声明您的事实:

private static bool IsInRange(int value, 
                              int lowerInclusiveBound,
                              int upperExclusiveBound,
                              string messageOnFailedValidation)
{  
    Debug.Assert(upperExclusiveBound > lowerInclusiveBound);

    if ...
}  

A good case could also be made on wether you should be checking that prompt in GetIntegerFromUser and messageOnFailedValidation in IsInRange are not empty or null strings, etc. 一个很好的例子也可以由上无论您应检查promptGetIntegerFromUsermessageOnFailedValidationIsInRange不为空或空字符串,等等。

Yeah, now we are really done. 是的,现在我们真的完成了。

You do not need the while loop inside the for loop. 您不需要for循环内的while循环。 just check each item for validity and break, if you encounter an invalid string, of course if all were valid (loop will go to the end) and i will be equal to length of your array: 只需检查每个项目的有效性和中断,如果遇到无效的字符串,当然,如果所有的字符串都是有效的(循环将结束),并且i等于数组的长度:

bool success = false;
do
{
     int i = 0;
     string[] valuesValuesCounter = Console.ReadLine().Split(' ');
     int[] valuescheckValuesCounter = new int[valuesValuesCounter.Length];
     for (int i = 0; i <= valuesValuesCounter.Length; i++)
        if(!int.TryParse(valuesValuesCounter[i], out valuescheckValuesCounter[i]) break;
     success = i == valuesValuesCounter.Length;
}while(!success);

Another way would be using Linq: 另一种方法是使用Linq:

do
{
    int i = 0;
    string[] valuesValuesCounter = Console.ReadLine().Split(' ');
    int[] valuescheckValuesCounter = new int[valuesValuesCounter.Length];
    success = valuesValuesCounter.All(x => int.TryParse(x, out valuescheckValuesCounter[i++]);
}while(!success);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM