71 votes

Dois-je créer un objet DateRange ?

Quelques-uns de mes objets de domaine contiennent des plages de dates sous la forme d'une paire de propriétés de dates de début et de fin :

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; }
}

Et je me retrouve souvent avec ça :

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);

La dernière m'a fait réfléchir... Devrais-je implémenter une classe DateRange ? Je n'en connais pas dans la BCL.

D'après mon expérience, le fait de rendre la hiérarchie des objets plus profonde complique souvent les choses. Ces objets sont envoyés aux rapports RDLC affichés par le contrôle ReportViewer, mais c'est secondaire. Je vais plier la vue au modèle plutôt que l'inverse. Nous ne sommes pas liés aux noms de propriété, cependant, et nous serions prêts à faire un compromis avec quelque chose comme :

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

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

Un avantage d'une classe DateRange serait la validation centralisée de la date de fin venant après la date de début, et cela simplifiera mes signatures de méthodes :

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

Je ne suis pas sûr qu'une classe DateRange ne me causera pas plus d'ennuis qu'elle n'en vaut la peine. Votre avis ?

Question secondaire : Ai-je manqué quelque part dans la BCL une classe de tuple générique à usage général ? Je sais qu'il y en a des très spécifiques qui flottent dans divers espaces de noms. Polluer mes signatures de méthodes du domaine public avec des types C5 est très, très sale.

0 votes

Je pense vraiment qu'une classe DateRange peut aider. J'ai commencé à écrire les bases d'une telle classe il y a quelque temps : adamjamesnaylor.com/2012/11/04/C-DateRange-Class.aspx

0 votes

@AdamNaylor : Votre lien semble être en panne ...

49voto

Jon Skeet Points 692016

Non, vous n'avez pas manqué un cours d'utilité générale.

J'ai un Range le type en MiscUtil qui pourrait vous intéresser - et cela permet certainement de simplifier DateTime manipulation. En ce qui concerne la réponse de Marc, je ne me souviens plus s'il s'agit d'un struct ou d'une classe - vous pouvez bien sûr le changer.

C'est agréable et facile à mettre en œuvre, grâce aux manigances génériques de Marc (en supposant que vous utilisiez .NET 3.5, au moins - c'est faisable avec 2.0 mais non pris en charge pour le moment) ;

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

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

(C'est aussi utiliser un tas de méthodes d'extension - plus utile pour le test que pour la production).

Pour répondre à l'autre point de la réponse de Marc, Heure de Noda sera certainement capable d'exprimer le concept de date de manière plus appropriée que l'API .NET, mais nous n'avons rien de tel qu'une plage pour le moment... C'est une bonne idée cependant - j'ai ajouté une fonction demande de fonctionnalité .

0 votes

J'ai modifié ma réponse acceptée pour la vôtre. Après avoir lu le code de Range<T>, j'ai décidé d'utiliser la version paramétrée de DateTime pour le noyau de ma classe de domaine DateRange. Cela me permet de faire 80% du chemin. Je vous remercie pour votre contribution.

0 votes

@JonSkeet ce serait bien d'avoir vos extensions sur nuget !

1 votes

@Shimmy : Eh bien, ces jours-ci, ils seraient des extensions en Heure de Noda à la place... mais MiscUtil est sur NuGet, certes en tant que prerelease : nuget.org/packages/JonSkeet.MiscUtil

18voto

Dans .NET 4.0 ou plus, le type Tuple<> a été ajouté pour gérer les valeurs multiples.

Avec le type tuple, vous pouvez définir votre propre combinaison de valeurs à la volée. Votre problème est très courant et est similaire à celui d'une fonction qui veut retourner plusieurs valeurs. Auparavant, vous deviez utiliser des variables externes ou créer une nouvelle classe uniquement pour la réponse de la fonction.

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

Quelle que soit la voie que vous choisissez, je pense que vous adoptez la bonne approche. Vous donnez un sens réel à ce que sont deux dates appariées. C'est du code auto-documenté et de la meilleure façon qui soit, directement dans la structure du code.

5voto

Marc Gravell Points 482669

Si vous travaillez beaucoup avec des dates, oui - une fourchette peut être pratique. Il s'agit en fait de l'un de ces cas, oh combien rares, où vous debe probablement l'écrire comme un struct (immuable). Notez, cependant, que "Noda Time" vous donnera probablement tout cela et plus encore (lorsqu'il sera terminé). J'ai déjà fait des logiciels de planification auparavant ; j'avais deux structures de ce type (pour des tâches légèrement différentes).

Notez qu'il n'existe pas de construction BCL pratique pour cela.

Pensez également à toutes les merveilleuses méthodes (et éventuellement aux opérateurs) que vous pouvez centraliser lorsque vous avez une plage ; "contient" (d'une date ? d'une autre plage ? incluant/excluant des limites ?), "intersects", offset-by (un intervalle de temps), etc. A définitif pour avoir un type pour le gérer. Notez qu'au niveau de l'ORM, cela est plus facile si votre ORM supporte les valeurs composées - je crois que NHibernate le fait, et peut-être EF 4.0.

0 votes

Étant donné qu'il s'agit d'une refonte visant à mieux gérer la manipulation des dates et des plages, je suppose qu'il est logique de lui donner une classe. Je regrette seulement de ne pas avoir utilisé NHibernate pour ce projet. Toutes ces heures perdues à écrire un DAL assez sympa si je puis dire, mais bien pâle en comparaison. Prochain projet, cependant. Je suis en train de jouer avec en ce moment pour m'habituer.

2voto

o.k.w Points 15721

Je ne connais pas de classe native .NET de la nature de DateRange. La plus proche est probablement DateTime+TimeSpan ou la combinaison DateTime/DateTime.

Je pense que ce que vous voulez est assez sain.

1voto

Frederik Gheysels Points 36354

Comme Mark et Jon l'ont déjà mentionné, je créerais ce type de valeur, qui est immuable. Je choisirais de l'implémenter en tant que struct, et d'implémenter la fonction IEquatable y IComparable interfaces.

En utilisant un ORM comme NHibernate, vous pourrez stocker le type de valeur dans une table qui représente une entité.

0 votes

Il ne devrait donc pas être difficile d'avoir un sous-objet DateRange mais de conserver la structure de table plate avec des colonnes de dates de début et de fin en utilisant NHibernate (Fluent) ? Je ne le pense pas, mais il est bon de le savoir à l'avance.

0 votes

Vous pouvez implémenter votre DateRange comme un objet de valeur, et l'utiliser dans NHibernate comme un "composant". Ensuite, vous pouvez effectivement conserver la structure de table plate avec des colonnes de dates de début et de fin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X