【问题标题】:Should I make a DateRange object?我应该创建一个 DateRange 对象吗?
【发布时间】:2010-12-23 03:53:42
【问题描述】:

我的一些域对象包含日期范围作为一对开始和结束日期属性:

public class Period {
  public DateTime EffectiveDate { get; set; }
  public DateTime ThroughDate { get; set; }
}

public class Timeline {
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
}

我发现自己有很多这样的事情:

abstract public int Foo(DateTime startDate, DateTime endDate);
abstract public decimal Bar(DateTime startDate, DateTime endDate);
abstract public ICollection<C5.Rec<DateTime, DateTime>> FooBar(DateTime startDate, DateTime endDate);

最后一个让我想知道......我应该实现一个 DateRange 类吗?我不知道 BCL 中有一个。

根据我的经验,使对象层次结构更深通常会使事情复杂化。这些对象确实会发送到 ReportViewer 控件显示的 RDLC 报告,但这是次要的。我会将视图弯曲到模型而不是反之。不过,我们不受属性名称的束缚,并且愿意妥协,例如:

public class DateRange {
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
}

Period p = new Period();
DateTime t = p.EffectiveDateRange.StartDate;

DateRange 类的一个好处是集中验证开始日期之后的结束日期,它会简化我的方法签名:

abstract public int Foo(DateRange dateRange);
abstract public decimal Bar(DateRange dateRange);
abstract public ICollection<DateRange> FooBar(DateRange dateRange);

我只是不确定 DateRange 类不会给我带来比其价值更多的麻烦。意见?

附带问题:我是否错过了 BCL 中某处的通用通用元组类?我知道在不同的命名空间中有一些非常具体的。用 C5 类型污染我的公共领域方法签名感觉非常非常肮脏。

【问题讨论】:

标签: .net model tuples date-range


【解决方案1】:

不,你没有错过通用课程。

我在MiscUtil 中有一个Range 类型,您可能会对它感兴趣 - 它肯定有助于简单的DateTime 操作。参考 Marc 的回答,我不记得这是一个结构还是一个类 - 当然欢迎您更改它。

由于 Marc 的泛型恶作剧(至少假设您使用的是 .NET 3.5 - 2.0 是可行的,但目前不支持),这很好而且很容易通过;

Range<DateTime> range = 19.June(1976).To(DateTime.Today);

foreach (DateTime date in range.Step(1.Days())
{
    // I was alive in this day
}

(这也使用了一堆扩展方法 - 对测试比生产更有用。)

为了解决 Marc 回答中的另一点,Noda Time 肯定能够比 .NET API 更恰当地表达日期的概念,但目前我们没有范围之类的东西......不过这是个好主意 - 我添加了 feature request

【讨论】:

  • 我已将接受的答案更改为您的答案。阅读 Range 代码后,我决定将 DateTime 参数化版本用于我的 DateRange 域类的核心。它让我成功了 80%。感谢您的贡献。
  • @JonSkeet 很高兴在 nuget 上有您的扩展!
  • @Shimmy:好吧,这些天它们将成为 Noda Time 的扩展......但 MiscUtil 在 NuGet 上,诚然是预发布版:nuget.org/packages/JonSkeet.MiscUtil
  • 而不是 range.Step(1.Days()) 尝试 range.Step(x => x.AddDays(1))
  • @Demodave:好吧,我可以更改库以接受这一点,但我认为它现在不会这样做。
【解决方案2】:

在 .NET 4.0 或更高版本中,添加了 Tuple 类型来处理多个值。

使用元组类型,您可以动态定义自己的值组合。您的问题很常见,类似于函数想要返回多个值时。以前,您必须使用 out 变量或创建一个新类来仅用于函数的响应。

Tuple<DateTime, DateTime> dateRange =
    new Tuple<DateTime, DateTime>(DateTime.Today, DateTime.Now);

无论你走哪条路,我认为你肯定采取了正确的方法。您正在为两个配对的日期赋予真正的意义。这是自我记录的代码,而且最棒的是,就在代码的结构中。

【讨论】:

    【解决方案3】:

    如果您对日期做了大量工作,是的 - 一个范围可以很方便。这实际上是您应该可能将其写为struct(不可变)的那些非常罕见的情况之一。但是请注意,“Noda Time”可能会给你所有这些以及更多(当它完成时)。我以前做过调度软件;我有几个这样的结构(用于稍微不同的工作)。

    注意,没有一个方便的 BCL 构造。

    另外 - 想想当你有一个范围时你可以集中的所有美妙的方法(可能还有运算符); “包含”(日期时间?另一个范围?包括/不包括限制?),“相交”,偏移量(时间跨度)等。 确定 有一个类型来处理它的情况.请注意,在 ORM 级别,如果您的 ORM 支持复合值,这会更容易 - 我相信 NHibernate 支持,可能还有 EF 4.0。

    【讨论】:

    • 鉴于这是为了更好地处理日期和范围操作而进行的重构,我认为给它一个类是有意义的。我只希望我在这个项目上使用 NHibernate。如果我自己这么说的话,所有这些时间都浪费在写一篇很好的文章上,但相比之下,DAL 就显得苍白无力。不过,下一个项目。我现在正在玩它以掌握它。
    【解决方案4】:

    正如 Mark 和 Jon 已经提到的,我会将其创建为不可变的值类型。我会选择将其实现为结构,并实现IEquatableIComparable 接口。

    当使用像 NHibernate 这样的 ORM 时,您将能够将值类型存储在表示实体的表中。

    【讨论】:

    • 所以拥有一个 DateRange 子对象应该不难,但使用 NHibernate (Fluent) 保持带有开始和结束日期列的平面表结构?我不这么认为,但最好提前知道。
    • 您可以将 DateRange 实现为值对象,并在 NHibernate 中将其用作“组件”。然后,您确实可以保留带有开始和结束日期列的平面表结构。
    【解决方案5】:

    我不知道任何具有 DateRange 特性的本机 .NET 类。最接近的可能是 DateTime+TimeSpan 或 DateTime/DateTime 组合。

    我认为你想要的还不错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-08
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-04
      • 1970-01-01
      • 2017-10-26
      相关资源
      最近更新 更多