Ce code n'a pas l'air propre et ceci si la condition peut augmenter
public int VisitMonth(int months) { int visit = 0; if (months <= 1) { visit = 1; } else if (months <= 2) { visit = 2; } else if (months <= 4) { visit = 3; } else if (months <= 6) { visit = 4; } else if (months <= 9) { visit = 5; } else if (months <= 12) { visit = 6; } else if (months <= 15) { visit = 7; } else if (months <= 18) { visit = 8; } else if (months <= 24) { visit = 9; } else if (months <= 30) { visit = 10; } else if (months <= 36) { visit = 11; } else if (months <= 48) { visit = 12; } else if (months <= 60) { visit = 13; } else { visit = 14; } return visit; }
Y a-t-il une meilleure solution à ce problème? Malheureusement, cette fonction n'est pas linéaire, il n'est donc pas facile de la coder de manière mathématique.
6 Réponses :
Peut-être en C # 8 (cette fonctionnalité n'est pas encore officielle, mais fonctionne dans les IDE récents si vous l'activez):
public int VisitMonth(int months) { switch (months) { case int j when j <= 1: return 1; case int j when j <= 2: return 2; case int j when j <= 4: return 3; // etc default: return 14; } }
Vous pouvez faire similaire dans plus tôt C #, puisqu'il s'agit d'une méthode:
int months = ...; int visit = months switch { int j when j <= 1 => 1, int j when j <= 2 => 2, int j when j <= 4 => 3, int j when j <= 6 => 4, int j when j <= 9 => 5, // ... _ => 42 // default };
L'une des fonctionnalités intéressantes de C # 8, j'aimerais qu'elle soit déjà publiée.
@Greg cela fonctionne dans VS 16 preview 2 - vous devez utiliser
, cependant - la dernière
ne suffit pas i >; il est également disponible dans l'interface utilisateur, sous le nom "C # 8.0 (beta)"
Je vais peut-être devoir le vérifier, je veux également utiliser la nouvelle application de nullité. Où aucune valeur par défaut est nulle, sauf indication contraire.
@ user6392608 alors vous devrez utiliser quelque chose de plus laid :)
Vous pouvez utiliser un dictionnaire pour stocker les mois sous forme de clés et les visites sous forme de valeurs.
static readonly (int Months,int Visit)[] monthsToVisits = new (int,int)[] { (1,1), (2,2), (4,3), (6,4) }; public int VisitMonth(int months) => monthsToVisits.First(x => months <= x.Months).Visit;
etc ...
Avec cela, vous pouvez facilement regarder la plus grande clé qui est juste plus que les mois que vous voulez juste vérifier, et la valeur associée.
int months = 42; int visit = monthsToVisits.Where(x => x.Key > months) .OrderBy(x => x.Key) .First().Value;
p >
Comme le disait @Marc Gravell, utiliser un dictionnaire est une solution très inefficace. Une meilleure approche serait un tableau statique.
var monthsToVisits= new Dictionary<int,int> { {1,1}, {2,2}, {4,3}, {6,4} };
cela semble convaincant, mais il est en fait très inefficace; le besoin de faire un OrderBy
etc à chaque fois est très cher, et vous n’utilisez jamais les fonctionnalités clés d’un Dictionary
First
plus précisément, quelque chose comme public int VisitMonth (int month) => monthToVisits.First (x => mois <= x.Months) .Visit;
où static readonly (int Month, int Visit) [] monthToVisits = new (int, int) [] {(1,1), (2,2), (4,3), (6,4)};
Devrait être plus approprié pour être réutilisé: Vous pouvez écrire une classe "Interval" avec la méthode "inRange" comme ceci:
public static readonly List<Interval<int>> range = new List<Interval<int>> { new Interval<int>(1, 0, 1), new Interval<int>(2, 1, 2), new Interval<int>(3, 2, 4), new Interval<int>(4, 4, 6), new Interval<int>(5, 6, 9), new Interval<int>(6, 9, 12), new Interval<int>(7, 12, 15), new Interval<int>(8, 15, 18), new Interval<int>(9, 18, 24), new Interval<int>(10, 24, 30), new Interval<int>(11, 30, 36), new Interval<int>(12, 36, 48), new Interval<int>(13, 48, 60), new Interval<int>(14, 60, int.MaxValue) }; var months = 5; var visit = range.Where(x => x.InRange(months)).Select(x => x.Visit).FirstOrDefault();
Et puis utiliser comme ceci:
public struct Interval<T> where T : IComparable { public T Start { get; set; } public T End { get; set; } public T Visit { get; set; } public Interval(T visit, T start, T end) { Visit = visit; Start = start; End = end; } public bool InRange(T value) { return ((!Start.HasValue || value.CompareTo(Start.Value) > 0) && (!End.HasValue || End.Value.CompareTo(value) >= 0)); } }
Cela ne fonctionnera que tant que la valeur de retour ( visite
) augmentera toujours linéairement pour chaque condition disponible (c'est-à-dire que visite
augmente de 1 dans chaque si
/ else if
block).
static void Main(string[] args) { Console.WriteLine($"0: {VisitMonth(0)}"); Console.WriteLine($"5: {VisitMonth(5)}"); Console.WriteLine($"60: {VisitMonth(60)}"); Console.WriteLine($"100: {VisitMonth(100)}"); Console.ReadLine(); }
Cette approche a l'avantage de ne pas avoir à définir la valeur de retour ( visite code>) est. Ce qui rend cela extensible dans le sens où si une exigence arrive là où il y a une nouvelle limite quelque part au milieu (par exemple 22), vous n'aurez pas à remapper la valeur de
visite
pour chaque condition car il est simplement dérivé en fonction de sa position dans le tableau.
Voici un exemple de ce fonctionnement:
static readonly int[] _monthLimits = new int[] { 1, 2, 4, 6, 9, 12, 15, 18, 24, 30, 36, 48, 60 }; public static int VisitMonth(int months) { int visit = 0; var maxMonths = _monthLimits[_monthLimits.Length - 1]; if (months <= maxMonths) { // Only iterate through month limits if the given "months" is below the max available limit for (var i = 0; i < _monthLimits.Length; i++) { if (months <= _monthLimits[i]) { visit = i + 1; break; } } } else { // The given "months" is over the max, default to the size of the array visit = _monthLimits.Length + 1; } return visit; }
Une façon de procéder serait de stocker les valeurs que vous comparez dans une Nous utilisons Cela réduit votre méthode à 2 lignes de code, et l'ajout d'une nouvelle condition comme List
, puis de renvoyer l ' index + 1
de premier élément qui remplit la condition mois , ou la liste
Count + 1
si aucun ne remplit cette condition. int?
pour nous permettre d'obtenir un résultat null
lorsque nous appelons FirstOrDefault
s'il n'y a pas de correspondance (nous ne utilisez int
, car default (int) == 0
afin que nous ne sachions pas si nous correspondions au premier élément à l'index 0
ou s'il y en avait aucune correspondance et la valeur par défaut 0
a été renvoyée). De cette façon, nous pouvons tester un résultat null
(ce qui signifie qu'il n'y a pas de correspondance), et retourner List.Count + 1
dans ce cas. if (mois est aussi simple que d'ajouter un
120
au valeurs
affectation: public static int Visit(int months)
{
var values = new List<int?> {1, 2, 4, 6, 9, 12, 15, 18, 24, 30, 36, 48, 60};
return (values.Select((v, i) => new {value = v, index = i})
.FirstOrDefault(i => months <= i.value)?.index ?? values.Count) + 1;
}
void Main() { var conditionsChain = new SimpleCondition(0, 1); conditionsChain.AddNext(new SimpleCondition(1, 1)) .AddNext(new SimpleCondition(2, 2)) .AddNext(new SimpleCondition(4, 3)) .AddNext(new SimpleCondition(6, 4)) .AddNext(new SimpleCondition(9, 5)) .AddNext(new SimpleCondition(12, 6)) .AddNext(new SimpleCondition(15, 7)) .AddNext(new SimpleCondition(18, 8)) .AddNext(new SimpleCondition(24, 9)) .AddNext(new SimpleCondition(30, 10)) .AddNext(new SimpleCondition(36, 11)) .AddNext(new SimpleCondition(48, 12)) .AddNext(new SimpleCondition(60, 13)) .AddNext(new SimpleCondition(14)); for (int i = 0; i < 62; i++) { Console.WriteLine($"{i}: {conditionsChain.Evaluate(i) - VisitMonth(i)}"); } } class SimpleCondition { private SimpleCondition _next; private int _key; private int _result; public SimpleCondition(int key, int result) { _key = key; _result = result; } public SimpleCondition(int result) : this(-1, result) { } public int Evaluate(int key) { if(_key == -1) { return _result; } if(key <= _key) { return _result; } else { if(_next == null) { throw new Exception("Default condition has not been configured."); } return _next.Evaluate(key); } } public SimpleCondition AddNext(SimpleCondition next) { return _next = next; } }
Mettez tout cela dans un tableau associatif et utilisez une boucle. Cela vous ramène à une instruction
if
.en C # 8, cela pourrait être une expression de commutateur avec des clauses
quand
...Pour clarifier, est-ce pour compter les visites tout au long de l'année ou sur un seul mois à quelle fréquence elles visitent?