【问题标题】:How to corporate condition statements in get;set properties for date time in c#?如何在获取中联合条件语句;在 C# 中设置日期时间的属性?
【发布时间】:2018-04-22 05:27:25
【问题描述】:

我在玩课程和约会时间。我想检查一个人的年龄是否超过 16 岁或不使用 get,set 属性。这是课程Test

class Test
    {
        private DateTime _Dob;
        public DateTime Dob
        {
            get { return _Dob; }
            set
            {
                if (DateTime.Now.Year - 16 < _Dob.Year)
                {
                    Console.WriteLine("error");
                }
                else
                {
                _Dob = value; }
            }
        }
        public Test(DateTime dob)
        {
            _Dob = dob;
        }

这里是主要的

Test dob = new Test(new DateTime(2007,12,01));
            dob.Dob();
            Console.ReadLine();

它在 main 中说 dob.Dob(); 不是一种方法。我该如何解决这个问题?

【问题讨论】:

  • 请注意,这不是一个好的做法。这种对吸气剂的检查是出乎意料的行为。如果我在 getter 上遇到错误,我认为 getting the value 出错了。您通常期望在 setter 或在这种情况下,在类的 constructor 上进行这种验证。顺便说一句,不确定这是否重要,但您的支票不是检查“他们是否超过 16 岁”。如果我出生于 2002 年 12 月,即使我只有 15 岁,我也能通过检查。
  • 抱歉,您正在使用setter,这是正确的。它不起作用的事实是因为您没有使用setter。

标签: c# datetime get set


【解决方案1】:

这里发生了几件事和许多思想流派。

  1. 如果您需要进行验证,最好不要在属性设置器中执行此操作。有一些很好的理由说明你不想在 setter 中做任何繁重的工作,如下所示
    • 因为属性“看起来”是字段,所以并不总是很明显它们可以抛出(设计使然)异常;而对于方法,程序员被训练来预期和调查异常是否是调用该方法的预期结果。
    • 许多 .NET 基础架构都使用 Getter,例如序列化程序和数据绑定(例如在 WinForms 和 WPF 中) - 在此类上下文中处理异常可能会很快成为问题。
    • 当您观察或检查对象时,调试器会自动评估属性获取器。这里的异常可能会令人困惑并减慢您的调试工作。出于同样的原因,在属性中执行其他昂贵的操作(如访问数据库)也是不可取的。
    • 属性通常用于链式约定:obj.PropA.AnotherProp.YetAnother - 使用这种语法,决定在哪里注入异常捕获语句变得有问题。
  2. 如果你要验证,可能值得扔ArgumentOutOfRangeException,如果你这样做,那么最好在一个方法中并将你的属性设置为private集,所以肮脏的小手指不要'不跳过验证

所以我的强迫症修改了你的课程Full Demo here

public class Test
{
    // make it a private setter so people don't try to set the property
    // without validation
    public DateTime DateOfBirth
    {
        get;
        private set;
    }

    // validation is probably best done outside of a property
    public void SetDateOfBirth(DateTime dateOfBirth)
    {
        if (dateOfBirth.Year > DateTime.Now.Year - 16)
        {
            throw new ArgumentOutOfRangeException("dateOfBirth", "Date of birth year cannot be after : " + (DateTime.Now.Year - 16));
        }

        DateOfBirth = dateOfBirth;
    }

    public Test()
    {
    }

    // if you really want one stage construction,
    // lets call the common DoB validation
    public Test(DateTime dateOfBirth)
    {
        SetDateOfBirth(dateOfBirth);
    }
}

使用示例

try
{
    Console.WriteLine("Testing invalid constrcutor");
    var test = new Test(new DateTime(2007, 12, 01));
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine(e.Message);
}

进一步阅读

Property Design

【讨论】:

  • 优秀的答案,是一个很好的例子,可以为客户提供他们需要的东西而不是他们想要的东西。
  • 你的第一点让 IMO 感到困惑 - 它说你不应该在 setter 中进行验证,但随后谈到了 getter 的各种情况i> 被调用。我认为在 setter 中进行验证很好,而且确实这样做很常见。不过,我不同意使用单独的方法 - 这很容易在没有在类中验证的情况下意外设置值。
  • @DaisyShipton 正如我所说的,那里有各种各样的思想流派,没有一种解决方案适合所有人,但我确实提供了尽可能多的信息。
  • @TheGeneral:但即使我同意不应该在属性设置器中进行验证(同样,我不这样做;例如,尝试将 List&lt;T&gt;.Capacity 设置为 -1),我想看到不同的论点。您的 4 个要点中有 3 个是关于何时调用 getters 的。这些要点只是不支持您关于何时调用 setter 的观点。我认为删除这些要点后答案至少会更好。
  • @DaisyShipton 是的,在许多地方,框架都会在属性中验证,但是您错过了重点,并且过于担心。我认为这是一个很好的经验法则(指导方针),因此(一如既往)应该由编写代码和审查代码的人决定。但是,在这种情况下,对于这个用户的问题,我会觉得很脏,就像我会从属性写入控制台并记下设置任何内容一样。
【解决方案2】:

为了解决您的代码未编译的原因:您正在调用Dob,就好像它是一种方法一样。不是;这是一个财产。你的属性有一个 getter 和一个 setter;它们在这些情况下被执行:

 Test test = new Test(new DateTime(...));
 DateTime fetched = test.Dob;  // Executes the getter
 test.Dob = new DateTime(...); // Executes the setter

这就是您的代码无法编译的方面。

一旦修复(例如,通过使用 DateTime dateOfBirth = test.Dob;,您仍然不会验证任何内容,因为这段代码:

public Test(DateTime dob)
{
    _Dob = dob;
}

这是直接分配给字段;它没有使用 setter - 你绕过了所有的验证。

接下来,您的验证使用_Dob.Year 而不是value,这意味着它正在验证之前设置的 属性值是否正确。

接下来,验证本身是不正确的,因为它不仅仅是检查年份部分而不考虑月份和日期。如果我的出生日期是 2002 年 4 月 22 日(我希望!),那么昨天我不会是 16 岁,但今天我会......你的代码不会发现差异。在下面的代码中,我假设您的“超过 16 岁”约束实际上是“至少 16 岁”。

最后,在控制台中打印错误消息并不是报告错误的惯用方式。使用异常报告错误。 (在某些情况下,您希望对多个项目分别执行验证并收集所有无效方面,但这超出了本问题的范围。)

我个人认为在属性设置器中验证很好,假设您需要它完全可用 - 另一种设计是将Dob 属性设置为只读,并在构造时验证。这将回避关于属性设置器是否应该抛出的整个讨论。但是,假设您确实想要一个属性设置器,下面是完整的代码,可以满足您的预期:

using System;

class Test
{
    private DateTime dateOfBirth;

    public DateTime DateOfBirth
    {
        get => dateOfBirth;
        set
        {
            // Alternative: if (DateTime.Now.AddYears(-16) > value)
            // They behave differently around leap years. Note that
            // currently this is sensitive to the system time zone
            // and the system clock.
            if (value.AddYears(16).Date > DateTime.Today)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(value), "User must be at least 16 years old");
            }
            dateOfBirth = value;
        }
    }

    public Test(DateTime dateOfBirth)
    {
        DateOfBirth = dateOfBirth;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Note: executed on April 22nd 2018
        Test test = new Test(new DateTime(1970, 1, 1));
        Console.WriteLine(test.DateOfBirth);
        test.DateOfBirth = new DateTime(2002, 4, 22); // Fine: 16th birthday
        Console.WriteLine(test.DateOfBirth);
        test.DateOfBirth = new DateTime(2002, 4, 23);
    }
}

最后一点:直接使用系统时钟(和系统时区)会使您的代码难以测试。我喜欢将时钟视为依赖项,具有一个带有系统时钟实现和一个假时钟实现的接口(例如IClock)。然后,您需要确定您感兴趣的时区,因为这会影响用户的年龄。 (例如,我可能 16 岁在纽约,但不在洛杉矶。)不过,这些是相当不同的问题。

【讨论】:

    【解决方案3】:

    当你使用 get 时;放;您不想将 Dob 作为函数调用,但就像访问类的普通属性一样。

    Test dob = new Test(new DateTime(2007,12,01));
    var myDob = dob.Dob;
    // myDob is what is return from the get in your get function
    

    【讨论】:

    • 谢谢,您的解决方案不会导致任何语法或逻辑错误,但它会返回空白。一些它是如何跳过条件的
    • @EddieVu:是的,因为你的构造函数直接赋值给了字段,然后这里的第二行代码调用了getter。没有什么在调用 setter。
    【解决方案4】:

    你没有使用你的 setter。

    像这样在你的构造函数中使用它:

    public Test(DateTime dob)
    {
        this.Dob = dob;
    }
    

    或者从外部调用setter:

    dob.Dob = new DateTime(2007,12,01);
    

    【讨论】:

      【解决方案5】:

      Dob 是类Test 的属性,而不是方法。

      您可以访问Dob 喜欢:

      var test = new Test(new DateTime(2010, 01, 01));
      var dob = test.Dob;
      

      但是,您已经注意到,当您调用 Test 构造函数时,您的 set 逻辑并未启动。我相信这是因为您的年龄检查不正确。当您调用构造函数时,set 逻辑未正确执行检查的原因是您正在比较 _Dob.Year 在它有值之前。您想使用 value 关键字来获取尝试设置的值。

      参见下面的示例(使用固定的年龄检查):

      public DateTime Dob
      {
          get
          {
              return _Dob;
          }
          set
          {
              if (GetAge(value) < 16)
              {
                  Console.WriteLine("You ain't 16!");
              }
              else
              {
                  _Dob = value;
              }
          }
      }
      
      private int GetAge(DateTime dob)
      {
          DateTime today = DateTime.Today;
          int age = today.Year - dob.Year;
          if (dob > today.AddYears(-age))
              age--;
      
         return age;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        • 2016-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-03
        相关资源
        最近更新 更多