C# 更有效的干净处理由枚举键控的多维数组的方法

C# 更有效的干净处理由枚举键控的多维数组的方法,c#,performance,generics,collections,C#,Performance,Generics,Collections,我有一种情况,我需要做许多get/set,在性能分析之后,这是我的应用程序中比较昂贵的部分之一。最初我使用的是一个字典,但在内部切换到一个锯齿状数组,这给了它一个显著的性能提升,不过我还是想看看是否有一种方法可以提高代码的性能,而不必放弃好用的语法 注意:调用Convert.ToInt32比调用强制转换要昂贵得多,而且由于通用约束TStatus:int对枚举不起作用,我必须将其实现为一个抽象类,因此如果此集合能够与任何现成的枚举一起工作,那就太好了 我还尝试为IEnumerable实现yield

我有一种情况,我需要做许多get/set,在性能分析之后,这是我的应用程序中比较昂贵的部分之一。最初我使用的是一个字典,但在内部切换到一个锯齿状数组,这给了它一个显著的性能提升,不过我还是想看看是否有一种方法可以提高代码的性能,而不必放弃好用的语法

注意:调用Convert.ToInt32比调用强制转换要昂贵得多,而且由于通用约束TStatus:int对枚举不起作用,我必须将其实现为一个抽象类,因此如果此集合能够与任何现成的枚举一起工作,那就太好了

我还尝试为IEnumerable实现yield,但是这实际上比仅仅填充列表要慢

    public abstract class LoanStatusVectorOverTime<TStatus> : ILoanStatusVectorOverTime<TStatus>
    where TStatus: struct
{
    protected static readonly TStatus[] LoanStatusTypes = (TStatus[])Enum.GetValues(typeof(TStatus));
    protected static readonly int LoanStatusCount = Enum.GetValues(typeof(TStatus)).Length;
    protected const int MonthsSinceEventCount = 25;

    private readonly object SYNC = new object();
    protected double[,] VectorDictionary { get; set; }
    public LoanStatusVectorOverTime()
    {
        this.VectorDictionary = new double[LoanStatusCount, MonthsSinceEventCount];
    }
    public double this[TStatus status, int monthsSince]
    {
        get
        {
            if (monthsSince >= MonthsSinceEventCount)
                return 0;
            return VectorDictionary[GetKeyValue(status), monthsSince];
        }
        set
        {
            if (monthsSince >= MonthsSinceEventCount)
                return;
            VectorDictionary[GetKeyValue(status), monthsSince] = value;
        }
    }
    public double SumOverStatus(TStatus status)
    {
        double sum = 0;
        foreach (var fromStatus in LoanStatusTypes)
        {
            int i = 0;
            while (i < MonthsSinceEventCount)
            {
                sum += VectorDictionary[GetKeyValue(fromStatus), i];
                i++;
            }
        }
        return sum;
    }
    public IEnumerator<KeyValuePair<Tuple<TStatus, int>, double>> GetEnumerator()
    {
        List<KeyValuePair<Tuple<TStatus, int>, double>> data = new List<KeyValuePair<Tuple<TStatus, int>, double>>();
        foreach (var fromStatus in LoanStatusTypes)
        {
            int i = 0;
            while (i < MonthsSinceEventCount)
            {
                var val = VectorDictionary[GetKeyValue(fromStatus), i];
                if (val != default(double))
                    data.Add(new KeyValuePair<Tuple<TStatus, int>, double>(new Tuple<TStatus, int>(fromStatus, i), val));
                i++;
            }
        }
        return data.GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    protected abstract int GetKeyValue(TStatus status);
    protected abstract ILoanStatusVectorOverTime<TStatus> Initalize();
    public ILoanStatusVectorOverTime<TStatus> Copy()
    {
        var vect = Initalize();
        foreach (var fromStatus in LoanStatusTypes)
        {
            int i = 0;
            while (i < MonthsSinceEventCount)
            {
                vect[fromStatus, i] = VectorDictionary[GetKeyValue(fromStatus), i];
                i++;
            }
        }
        return vect;
    }
    public double SumOverAll(int monthsSince = 1)
    {
        double sum = 0;
        foreach (var status in LoanStatusTypes)
        {
            sum += this[status, monthsSince];
        }
        return sum;
    }
}
    public class AssetResolutionVector : LoanStatusVectorOverTime<AssetResolutionStatus>
{
    protected override int GetKeyValue(AssetResolutionStatus status)
    {
        return (int)status;
    }

    protected override ILoanStatusVectorOverTime<AssetResolutionStatus> Initalize()
    {
        return new AssetResolutionVector();
    }
}
    var arvector = new AssetResolutionVector();
    arvector[AssetResolutionStatus.ShortSale, 1] = 10;

如果enum到int的转换占用了大量的时间,请确保不要在内部循环的每次迭代中进行转换。下面是一个为SumOverStatus方法缓存转换的示例:

额外提示:虽然它可能不会提高性能,但可以通过使用Func转换器避免将类抽象化。下面介绍了如何将转换器作为属性公开—构造函数参数也可以正常工作:

public class LoanStatusVectorOverTime<TStatus>
{
    public Func<TStatus, int> GetKeyValue { get; set; }
}

// When the object gets instantiated
loanStatusVectorOverTime.GetKeyValue = status => (int)status;

听起来这里有两个不同的问题:

当使用抽象方法或委托进行转换时,从枚举转换为整数会产生开销。这里有两个选项:

A.从类中取出泛型参数,并根据需要对枚举类型进行硬编码,以制作类的多个副本

B.让你的访问器取两个整数,而不是一个枚举和一个整数,让客户端从枚举到整数进行廉价转换

获取/设置过程中占用了大量时间。这可能不是因为get/set效率低下,而是因为调用get/set的次数太多。建议:

A.按月份或状态对操作进行分组,可能使用嵌套数组重新构造数据结构,并编写有效的循环

B.降低执行所有获取和设置的代码的计算复杂性。通过退一步并规划出您的程序,您可能会发现更有效的算法


您还可以给出ILoanStatusVectorOverTimepublic接口ILoanStatusVectorOverTime:IEnumerable的代码,其中TStatus:struct{double this[TStatus status,int monthsince]{get;set;}double SumOverAllint monthsince=1;ILoanStatusVectorOverTime Copy;}@杰森·林德:你可以通过点击编辑按钮为你的问题添加额外的代码。这样读界面会容易得多。我没有看到代表们提出的锯齿状排列奇怪的建议,尽管我不确定是否每次都要调用它。至于状态的总和,另一个很好的建议虽然很少被称为get/set,但在绝大多数情况下,get/set就是所谓的。
public class LoanStatusVectorOverTime<TStatus>
{
    public Func<TStatus, int> GetKeyValue { get; set; }
}

// When the object gets instantiated
loanStatusVectorOverTime.GetKeyValue = status => (int)status;