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