【问题标题】:Creating calendar with array and loops using C#使用 C# 创建带有数组和循环的日历
【发布时间】:2018-09-20 01:12:39
【问题描述】:

我正在尝试创建一个应用程序,当输入 2016 年的日期(1 月 1 日、9 月 3 日等)时告诉您星期几(星期一、星期二等;)输入是日期,输出是该日期的星期几。它必须使用循环创建,例如 for 循环和 if 语句。我不能使用库函数。开始日期是 2016 年 1 月 1 日,星期五。这是我到目前为止所拥有的,我很迷茫:

    public enum Days : byte { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
    public enum Months : byte { January, February, March, April, May, June, July, August, September, October, November, December };
    class Program
    {
        public static void Main(string[] args)
        {
            const int numDays = 7;
            const int numMonths = 12;
            Months month = Months.Janurary;
            int monthCounter = 1;
            int[] daysinMonth = new int[numMonths] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
            int[] daysofWeek = new int[numDays] {1, 2, 3, 4, 5, 6, 7};
            Days dayOfWeek = Days.Sunday;
            dayOfWeek = Days.Friday;
            foreach (month in Months )
            {

我的简介是:

仅使用我们迄今为止介绍的工具,使用嵌套循环和比较,找到 2016 年特定日期的工作日名称,从 2016 年 1 月 1 日星期五开始工作。

【问题讨论】:

  • 正在尝试实现 c# DayOfWeek?
  • new DateTime(2016, 1, 16).DayOfWeek 会给你同样的结果,所以..
  • 这个问题看起来太宽泛了,事实上你还没有真正问过问题。你具体是在坚持什么?
  • 用循环来做吗?有一个algorithm
  • 如果您愿意/能够编辑问题,包括关于“我们迄今为止涵盖的工具”的提示会有所帮助。此外,一般生活提示,您不必为每个工作使用每个工具。

标签: c# arrays


【解决方案1】:

既然你不需要代码,我就给你一些解释,只给你一点代码。然后,一旦这一切都完成了,在它的底部会有完整的代码,它将为您提供任意日期所需的一切。

但是,您只对 2016 年感兴趣。因此,如果您确实想要计算特定年份的星期几,那么您所需要的(来自完整代码)就是类声明和两个功能:

  • GetDayNumberFromDate
  • GetDayOfWeekFromDate

解释和一小段代码

我会在一个静态类中完成所有这些。您不需要任何instance,只需几个函数/方法即可。

在该类中,我将声明并初始化三个私有变量:

  • 12 个整数的数组,每个整数代表每个月的天数。请记住,数组索引从零开始,因此您需要从月份数 (1-12) 中减去一个来获得数组索引 (0-11)。我还建议您发表评论,指出 2016 年是闰年,这就是为什么 2 月有 29 天
  • 一个私有的const整数(可能命名为YearOfInterest),设置为2016。它代表数字2016的符号别名。常量使你的代码更容易维护(考虑如果你的老师改变主意说“不,为 2014 年做”,并且您在代码中的 6 或 8 个位置使用了 2016 - 如果您有 const,您只需要更改其中一个位置。
  • 表示 2016 年 1 月 1 日是星期五这一事实。我会使用 System.DayOfWeek 枚举(是的,它是库代码,但你所做的任何事情都将是一个字符一个字符的重复)。如果你愿意,你可以使用你的 YearOfInterest const int 并做类似的事情(如果你的老师改变了对这一年的想法,这将使事情顺利进行)

这将是一个非常简单的声明,如下所示:

 private readonly static DayOfWeek Jan1DayOfWeek = new DateTime (YearOfInterest, 1, 1).DayOfWeek;

我认为这比仅仅说:

private readonly static DayOfWeek Jan1DayOfWeek = DayOfWeek.Friday;

请记住,枚举从零开始编号(默认情况下),因此星期日为 0,星期六为 6。

完成后,我将创建两个函数:

  • 计算某个日期的 Day Number(即,一年中到该日期为止的天数(1 月 1 日的天数是 1,2 月 3 日的天数是 34 )。
  • 调用您的 Day Number 函数(即获取整数)并返回一周中的某一天。

第一个函数(我们称之为GetDayNumberFromDate)采用两个整数日期(月和日)做了三件事:

  • 它会检查您的先决条件。由于这两个数字都是用户输入的,并且用户犯了错误,因此您需要确保两个数字都等于或大于 1、月数等于或小于 12、日数等于或小于一个月中的天数(您可以方便地知道 - 它在您的数组中(请记住在索引该数组时减去一个)。如果不满足任何这些先决条件,则抛出异常。这很重要。如果如果有人输入了错误的数字,您可能会从数组中索引(这会引发异常,但没有关于它发生原因的信息)。检查前置条件总是好的
  • 它循环遍历您的“一个月中的天数”数组,从数组的开头到输入月份的前一个月(减一)对天数求和。如果有人输入 4 月 4 日,您要添加 1 月 (31)、2 月 (29) 和 3 月 (31) 的天数。这将使您到达月初(嗯,上个月的最后一天)。
  • 它会在输入的月份中添加日期 - 让您返回当前日期。

由于您希望在 for 循环上进行指导,因此该循环最终将如下所示:

int dayCount = 0;
for (int i = 0; i < month - 1; ++i) {
    dayCount += daysPerMonth[i];
}

在循环结束时,dayCount 会告诉您从年初到输入日期前一个月月底的天数。当您添加输入的月份中的某一天时,您会得到day number

最后,最后一个函数(称为GetDayOfWeekFromDate)也采用相同的两个整数(月份编号和月份编号)。它调用GetDayNumberFromDate 来获取输入日期的天数,减一(因为从 0 开始的计算比从 1 开始的计算要容易得多)并添加与我们在开头声明的 Jan1DayOfWeek 等效的整数((int) Jan1DayOfWeek )。

有了这个,我会用那个数字做一个“模数 7”运算 (thatNumber % 7)。模数是计算整数除法余数的结果。这将为您提供一个介于 0 和 6 之间的数字。如果您将该数字重新转换为 DayOfWeek,它将为您提供周日和周六之间的一周中的一天。对该结果致电ToString,您就会得到结果。

如果结果不合格(尤其是差一分),请不要感到惊讶。第一次做到这一点很棘手。这就是调试的用武之地。

顺便说一下,我不会尝试解析像“1 月 1 日”这样的日期。你真的需要注意缩写(Jan,Feb,...),你会让那些不会拼写“February”的人不高兴。只需使用月份和日期整数。

在所有这些中您会注意到的一件事是没有嵌套循环。没有必要这样做,嵌套循环可能很昂贵。此代码循环过去的月数(即少于 12 次)。如果您要循环一个月和一个月中的几天,您将循环 12 次 30 次(即大约 366 次)。 12 是一个较小的数字

完整代码如下:

我不知道你学到了什么,所以这主要是在 2005 年左右的非常基本的 C# 中。

首先是一个带有一些基本“常量”的静态类:

static class FigureDate {
    private static readonly int[] _leapYearDaysPerMonth = new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    private static readonly int[] _normalYearDaysPerMonth = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    private const int YearZero = 1900;
    //Sunday is zero in DayOfWeek
    private readonly static DayOfWeek _day0 = new DateTime(YearZero, 1, 1).DayOfWeek;
}

(顺便说一句,DayOfWeek 是在 System 命名空间中定义的 enum,因此您可以免费获得它。星期日是 0)

我将在该类中添加更多内容 - 所有内容(即下面的所有函数)都在其中。

首先,特定的年份是闰年(这绝非易事):

public static bool IsLeapYear (int year) {
    if (year % 400 == 0) {
        return true;
    }
    if (year % 100 == 0) {
        return false;
    }
    //otherwise
    return (year % 4) == 0;
}

然后我计算从“第 0 天”开始的闰年如何过去(幸运的是,1900 年不是闰年,因此减少了一个错误的可能性):

public static int NumLeapYearsToStartOfYear(int year) {
    if (year < YearZero) {
        throw new ArgumentException($"Only Years from {YearZero} onwards can be evaluated");
    }
    //do it brute force
    int count = 0;
    for (var y = YearZero; y <= year; ++y) {
        if (IsLeapYear(y)) {
            ++count;
        }
    }
    return count;
}

有了这个,我可以算出从“第 0 天”到感兴趣的年初有多少天:

 public static int DaysToStartOfYear(int year) {
     return (year - YearZero) * 365 + NumLeapYearsToStartOfYear(year);
 }

现在,换个角度,找出有趣的一天是什么“天数”(记住,2 月 3 日是第 34 天)。

public static int GetDayNumberFromDate (int year, int month, int day) {
    int[] daysPerMonth = IsLeapYear(year) ? _leapYearDaysPerMonth : _normalYearDaysPerMonth;
    if (month < 1 || month > 12) {
        throw new ArgumentException($"Month number ({month}) is invalid");
    }
    if (day < 1 || day > daysPerMonth[month - 1]) {
        throw new ArgumentException($"Day/Month combination invalid for month:{month}, day:{day}");
    }
    int dayCount = 0;
    for (int i = 0; i < month - 1; ++i) {
        dayCount += daysPerMonth[i];
    }
    return dayCount + day;
}

最后把它们放在一起。正如您可能想象的那样,此代码中有一个问题。我使用日期的“天数”,它在 1...N 范围内,我们真的希望它是 0...N-1。所以,我减去 1。

public static DayOfWeek GetDayOfWeekFromDate (int year, int month, int day) {
    int totalDays = DaysToStartOfYear(year) + GetDayNumberFromDate(year, month, day) - 1 + (int)_day0;
    return (DayOfWeek)(totalDays % 7);
}

如果您调用该函数,然后在结果上调用ToString,它应该工作。我只是对它进行了非常轻微的测试。

不要仅仅将其作为您的作品提交。把它拆开,看看你能不能做得更好。如果您知道如何编写单元测试,请为每个函数编写一个测试(我通常会这样做,但今晚不会)。弄清楚我向你展示了我展示给你的功能,我展示给你的顺序是我展示给你的。为什么我在我做的地方而不是到处检查论点?为什么我在使用它的地方使用readonly,而在YearZero 使用const?一些代码非常蛮力 - 看看你是否可以做得更好(一旦你有足够好的代码,获得更好的代码会容易得多)。

最后,你应该能够解释每一行代码了。

【讨论】:

  • 对不起,只看2016年的评论是N多cmet之一,我没看过。我不知道你为什么需要嵌套循环。如果您使用 2016 作为开始日期,请注意我没有使用闰年的 YearZero 进行测试
【解决方案2】:

现在我知道你的任务完全集中在 2016 年,你的简报要求你使用嵌套循环,以及你提供的代码,我建议这样做:

1) 定义 targetMonth 和 targetDate(例如 9 月 3 日) 2) 确保 daysOfWeek 从星期五开始 3)使用这个伪代码:

foreach mMonth in Months
    for dayOfM = 1 to daysinMonth[month] (inclusive)
        if month == targetMonth AND dayOfM == targetDate
            print dayOfWeek
            return
        else
            dayOfWeek = (dayOfWeek + 1) % 7

有用的功能:

  • Enum.GetValues(typeof(Months)) 会将 Months 中的所有值作为数组返回。
  • .OfType&lt;Months&gt;()(包括 using System.Linq 命名空间) - 您可以使用它来将 Enum.GetValues() 返回的 Array 转换为强类型 Months 对象。
  • 您可以使用(int)dayofWeekdayOfWeek 转换为整数,然后使用(Days)5(或您拥有的任何整数值)返回。
  • Modulus % 将返回计算的余数。 1 % 7 = 1, 2 % 7 = 2, ..., 7 % 7 == 0 - 这将允许您循环一周中的几天。

这是我在 C# 中提出的解决方案,并返回到伪代码,以免为您提供复制/粘贴的答案。希望对你有帮助。

【讨论】:

    【解决方案3】:

    您可以采用的一种方法是计算从年初到指定日期的总天数,将该数字除以 7(一周中的总天数)后的余数,然后添加一年中第一天的数值。

    首先,我们会在他们输入的月份之前添加每个月的总天数。为此,我们首先需要确定他们输入的月份,然后将其转换为可以用作daysInEachMonth 数组的索引的整数。

    我们可以首先使用Enum.Parse 方法获取他们输入的字符串的枚举(该方法接受Enum 的类型用于解析(在我们的例子中为Month),以及类似“一月”,并返回 enum Month 值)。然后我们可以将此值转换为int。默认情况下,Enums 以 0 开头并以 1 递增,因此 Januray0December11

    int monthNumber = (int) Enum.Parse(typeof(Month), month, true);
    

    使用这个值,我们可以遍历每个月之前的每个月,并将总天数相加到这个月:

    for (int i = 0; i < monthNumber; i++)
    {
        daysFromStartOfYear += daysInEachMonth[i];
    }
    

    接下来我们将他们输入的月份中的某天加上并减去 1(我们需要减去 1,因为我们正在处理数组索引和 enum 值,它们都以 0 开头):

    daysFromStartOfYear += dayDate - 1;
    

    现在我们需要通过添加一年中第一天的数值来调整我们的数字(其中Sunday0Saturday6)。如果这没有意义,请记住如果他们输入了January 1,那么我们的daysFromStartOfYear 值将是0,但我们知道一年的第一天是Friday(其中有一个int 5 的值)。所以我们知道我们需要将5 添加到结果中,以说明第一天不是从0(星期日)开始的。

    Day firstDayOfYear = Day.Friday;
    daysFromStartOfYear += (int) firstDayOfYear;
    

    现在剩下要做的就是将此数字除以7(一年中的天数),得到余数,然后将该数字转换为Day 枚举中的值。如果这没有意义,请记住一个月中的每个Tuesday 都有一个日期是7 加上该月第一个Tuesday 的值的倍数。通过得到除以7 后的余数,我们最终得到一个介于06 之间的数字,它将映射到我们的Day 枚举。

    要得到一个数的余数除以另一个数,我们可以使用模运算符%

    int remainder = daysFromStartOfYear % numDaysInWeek;
    

    当我们得到这个数字后,我们可以将它转换为我们的枚举以获取值:

    Day answer = (Day) remainder;
    

    希望这足以让您入门。如果您遇到特定问题,请尝试并回来,或者随时在下面的 cmets 中提问。

    【讨论】:

      【解决方案4】:

      如果您只想显示星期几,可以这样做:

      DateTime date = new DateTime(2016, 1, 16);
      Console.WriteLine(dateValue.ToString("dddd"));  // outputs Friday
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-17
        • 2018-03-15
        • 2017-03-04
        • 1970-01-01
        相关资源
        最近更新 更多