我正在上下课
public class MyDictionary : SortedList<int, MyData>
{
}
目前Key
中的SortedList
代表年份数字,例如2014年,2015年,2016年等。Value
代表年度数据。
现在我有一个新的要求,即每年Value
是不够的,这个类应该支持更精细的粒度。
新粒度如下所示:
当然,MyDictionary
的一个实例应代表一个时间范围,例如SortedList<Yearly, MyData>
,SortedList<Monthly, MyData>
。
MyDictionary
的数据跨越了几年。这意味着我无法使用,例如每月粒度的月份数。例如:
2014-12
2015-01
2015-02
...
2015-12
如您所见,数字12在列表中是两次。
我的问题是,我不知道Key
使用什么数据类型以及如何访问Values
中的MyDictionary
以满足新要求。
有什么想法吗?
2016年2月24日修改:
我必须在原始问题上添加更多信息。
Values
对array indexer []
的访问必须经过运行时优化。它将在很短的时间内被召唤数百万次。MyDictionary
的类使用DateTime
对象来访问Values
。例如:public class ClientClass
{
public void AccessMyDictionary(DateTime date)
{
MyData data = MyDictionary[date.Year];
// Do something with data
}
}
在我看来,最明显的事情是将DateTime
作为索引器数据类型。然后在MyDictionary
类中创建一个索引器来处理粒度。例如:
public enum Period
{
Yearly,
Quarterly,
Monthly,
Weekly,
Daily
}
public class MyDictionary
{
private Period period;
private SortedList<DateTime, MyData> sortedList;
public MyDictionary(Period period)
{
this.period = period;
sortedList = new SortedList<DateTime, MyData>();
}
public MyData this[DateTime i]
{
get
{
// Implement here an algorithm for granularity, similar to the one of Tomas Lycken in the 1st answer
}
set
{
// Implement here an algorithm for granularity, similar to the one of Tomas Lycken in the 1st answer
}
}
}
你怎么看?该运行时是否已优化?
非常感谢 康斯坦丁
答案 0 :(得分:2)
我将为各种粒度定义一些新的值对象,所有这些都来自公共基类Period
。然后,您可以使用这些键。例如,
public abstract class Period { }
public class Quarter : Period
{
public int Quarter { get; }
public int Year { get; }
public Quarter(int year, int quarter)
{
if (year < 1800 || year > DateTime.UtcNow.Year)
{
throw new ArgumentOutOfRangeException(nameof(year));
}
if (quarter < 1 || quarter > 4)
{
throw new ArgumentOutOfRangeException(nameof(quarter));
}
Year = year;
Quarter = quarter;
}
}
当然,您为Year
定义了类似的类型(只有一个属性),Month
(有一年和一个月,月份必须在1到12之间) ,Week
(验证变得有点棘手,因为并非所有年份都有相同的周数),Day
(不要忘记允许闰年!)。
然后,您还为这些类型定义了相等和散列,以便在它们的属性相等时,它们是相等的。 (This是关于这个主题的好读物!)对于Quarter
,我会做类似的事情
public class Quarter
{
// properties and constructor ommitted
public override bool Equals(object other)
{
if (!(other is Quarter))
{
return false;
}
var quarter = (Quarter)other;
return quarter.Year == Year && quarter.Quarter == quarter;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
// The two hard-coded digits below should be primes,
// uniquely chosen per type (so no two types you define
// use the same primes).
int hash = (int) 2166136261;
// Suitable nullity checks etc, of course :)
hash = hash * 16777619 ^ Quarter.GetHashCode();
hash = hash * 16777619 ^ Year.GetHashCode();
return hash;
}
}
}
根据您将要使用这些内容的其他方式,您可能还需要override ==
and/or !=
。
现在,这些类型完全可用作字典中的键,因此您可以执行
var quarterlyReport = new SortedList<Quarter, Data>();
如果你想避免手动定义Equals
和GetHashCode
,那么.NET中的大多数关联集合都有一个构造函数,它为密钥类型采用相等比较器,为你处理这个问题。 SortedList<TKey, TValue>
has one too,因此您可以为每个时段创建一个吊坠类型,而不是覆盖Equals
和GetHashCode
,而不是
public class QuarterComparer : IComparer<Quarter>
{
int IComparer<Quarter>.Compare(Quarter p, Quarter q)
{
return p.Year < q.Year
? -1
: p.Year == q.Year
? p.Quarter < q.Quarter
? -1
: p.Quarter == q.Quarter
? 0
: 1
: 1;
}
public int Compare(Quarter p, Quarter q)
{
return (this as IComparer<Quarter>).Compare(p, q);
}
}
并将其传递给已排序列表的构造函数:
var quarterlyData = new SortedList<Quarter, MyData>(new QuarterComparer());