SortedList的动态键<key,value =“”>

时间:2016-02-19 06:29:23

标签: c#

我正在上下课

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日修改:

我必须在原始问题上添加更多信息。

  1. 粒度仅在运行时已知
  2. 通过Valuesarray indexer []的访问必须经过运行时优化。它将在很短的时间内被召唤数百万次。
  3. 使用MyDictionary的类使用DateTime对象来访问Values。例如:
  4. 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
            }
        }
    }
    
    你怎么看?该运行时是否已优化?

    非常感谢 康斯坦丁

1 个答案:

答案 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>();

如果你想避免手动定义EqualsGetHashCode,那么.NET中的大多数关联集合都有一个构造函数,它为密钥类型采用相等比较器,为你处理这个问题。 SortedList<TKey, TValue> has one too,因此您可以为每个时段创建一个吊坠类型,而不是覆盖EqualsGetHashCode,而不是

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