如果我要这样做,我会采取完全不同的策略。我会有一个代表一天测量值的类。然后,我将有一个代表一个月的每日测量值的类 - 它将包含每日测量实例的字典,按月中的某天索引。
所以,开始做一些家务。我喜欢象征性地工作,...
public enum Months
{
January = 1,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
}
现在我可以使用Months.September 而不是9。
现在我将创建一些东西来跟踪白天的温度测量。我将为每次测量添加时间戳(使用TimeSpan)。我将在Dictionary<TimeSpan, float> 中跟踪测量结果,并根据记录时间跟踪每个测量结果。首先是简单的部分:
public class DailyTemperatureMeasurements : IEnumerable<(TimeSpan time, float temperature)>, IEnumerable<float>
{
private readonly Dictionary<TimeSpan, float> _measurements = new Dictionary<TimeSpan, float>();
private IEnumerable<float> _enumerableImplementation;
public void Add(TimeSpan time, float temperature)
{
_measurements.Add(time, temperature);
}
IEnumerator<float> IEnumerable<float>.GetEnumerator()
{
foreach (var measurement in _measurements)
{
yield return measurement.Value;
}
}
public IEnumerator<(TimeSpan time, float temperature)> GetEnumerator()
{
foreach (var measurement in _measurements)
{
yield return (measurement.Key, measurement.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<float>) this).GetEnumerator();
}
}
请注意,我通过两种方式创建了 Enumerable 类。您可以枚举它并获取当天的所有温度(没有时间戳),或者您可以获得表示时间戳/测量值对的 Tuples。我不使用其中任何一个,但您可能会觉得它很方便。
另外,因为类是可枚举的并且有一个 Add 方法,你可以使用Collection Initialization。同样,我不使用它,但你可以使用。
最难的部分是把价值观拿出来。我怀疑您可能希望使用插值来获得在特定时间对测量的最佳估计。我太懒了,所以我在要求的时间之前选择了一个。此外,虽然字典通常按添加顺序返回项目,但 Dictionary<TKey, TValue> 类明确表示枚举的顺序是未定义的(所以我通过所有值来解决这个问题)。您可能想看看另一种方法来做到这一点。
那么,让我们将这个函数添加到DailyTemperatureMeasurements 类...
public float GetMeasurementAt(TimeSpan time)
{
if (!_measurements.Any())
{
throw new Exception("There are no measurements registered in this object");
}
if (_measurements.ContainsKey(time))
{
return _measurements[time];
}
//you may want to do interpolation, but I'm keeping it simple
//find the last measurement before the requested time
var lastClosestTime = default(TimeSpan);
foreach (var measurement in _measurements)
{
var timeToMeasurement = time - measurement.Key;
if (timeToMeasurement < time - lastClosestTime && timeToMeasurement > default(TimeSpan))
{
lastClosestTime = measurement.Key;
}
}
if (lastClosestTime == default)
{
return 0.0f;
}
return _measurements[lastClosestTime];
}
现在我正在跟踪一天的所有测量结果。让我们跟踪他们一个月:
正如您所指出的,并非所有月份都有 31 天。它变得比这更复杂(二月)。我们不想要坏数据,所以我有这两个小私人工人,我将把它们添加到我的每月测量课程中:
private static readonly Dictionary<Months, int> MonthLengths = new Dictionary<Months, int>
{
{Months.January, 31},
{Months.February, 28},
{Months.March, 31},
{Months.April, 30},
{Months.May, 31},
{Months.June, 30},
{Months.July, 31},
{Months.August, 31},
{Months.September, 30},
{Months.October, 31},
{Months.November, 30},
{Months.December, 30},
};
private static int GetMonthLength(Months month, int year)
{
if (month != Months.February)
{
return MonthLengths[month];
}
//otherwise, month is February, so...
if (year % 400 == 0)
{
return 29;
}
if (year % 100 == 0)
{
return 28;
}
if (year % 4 == 0)
{
return 29;
}
return 28;
}
这样,如果有人尝试添加 2100 年 4 月 31 日或 2 月 29 日的数据,我可以抛出异常。
跟踪每月的测量结果实际上非常简单:
public class TemperatureMeasurements
{
private readonly Dictionary<int, DailyTemperatureMeasurements> _dailyMeasurements = new Dictionary<int, DailyTemperatureMeasurements>();
public TemperatureMeasurements(int year, Months month)
{
//you may want to sanity check the year value (no years less than now, no years greater than 2500) - maybe
Year = year;
Month = month;
}
public int Year { get; }
public Months Month { get; }
public void Add(int day, TimeSpan time, float measurement)
{
if (day < 1 || day > GetMonthLength(Month, Year))
{
throw new ArgumentOutOfRangeException(nameof (day), "Day value must be within range for month: {_month}");
}
if (!_dailyMeasurements.ContainsKey(day))
{
_dailyMeasurements.Add(day, new DailyTemperatureMeasurements());
}
_dailyMeasurements[day].Add(time, measurement);
}
public void Add(TimeSpan time, float measurement)
{
var now = DateTime.Now;
var monthToday = (Months)now.Month;
if (monthToday != Month)
{
throw new ArgumentException($"This overload of {nameof(Add)} can only be used with today's date is within the month associated with this {nameof(TemperatureMeasurements)} instance ");
}
var dayToday = now.Day;
Add(dayToday, time, measurement);
}
public void Add(float measurement)
{
var now = DateTime.Now.TimeOfDay;
Add(now, measurement);
}
public float GetMeasurementAt(int day, TimeSpan time)
{
if (!_dailyMeasurements.ContainsKey(day))
{
throw new ArgumentOutOfRangeException(nameof(day), "No measurements for {Month} {day} have been recorded");
}
return _dailyMeasurements[day].GetMeasurementAt(time);
}
//This is where that MonthLengths dictionary and GetMonthLengths function go
}
您需要提供其中一个将用于的月份和年份(以便可以正确计算一个月中的天数)。
数据保存在字典中,其中键是月份中的某天(1..[月份中的天数]),值是DailyTemperatureMeasurements(我们之前创建的类)实例。
您可以通过多种方式添加数据:
- 您可以指定月份中的日期、时间和测量值
void Add(int day, TimeSpan time, float measurement)
- 您可以只指定时间和测量值(假设您的意思是今天)
void Add(TimeSpan time, float measurement)
- 您可以只指定测量值(假设您的意思是今天和现在的日期和时间)
void Add(float measurement)
要获取值,请调用GetMeasurementAt,传递月份中的日期和时间。它将呼叫委托给请求日期的适当DailyTemperatureMeasurements。
此代码经过了非常轻微的测试。你会想要更多地测试它。它应该使记录测量更容易。