C# 对属性执行通用且快速的计算
目标是创建一个方法,该方法通常以性能方式对对象列表的属性执行计算。下面是整个测试代码:C# 对属性执行通用且快速的计算,c#,loops,generics,properties,C#,Loops,Generics,Properties,目标是创建一个方法,该方法通常以性能方式对对象列表的属性执行计算。下面是整个测试代码: using System; using System.Collections.Generic; namespace TestApp { public class Minute { public DateTime DateTimeUtc { get; set; } public float Source { get; set; }
using System;
using System.Collections.Generic;
namespace TestApp
{
public class Minute
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
public float Mult2 { get; set; }
public float Mult3 { get; set; }
public float Mult4 { get; set; }
}
class Program
{
public static List<Minute> Minutes = new List<Minute>();
static void Main(string[] args)
{
for (int i = 1; i < 10000000; i++)
{
Minute newMinute = new Minute();
newMinute.Source = i;
Minutes.Add(newMinute);
}
GenerateMult2(Minutes, 2); // 160 ms
GenerateMult2Generic(Minutes, typeof(Minute), nameof(Minute.Source), nameof(Minute.Mult2),2); // 4300 ms
}
public static void GenerateMult2(List<Minute> Minutes, int multiplier)
{
for (int i = 0; i < Minutes.Count; i++)
{
// Simplified calculation, there will eventually be a lot more code that goes here!
Minutes[i].Mult2 = Minutes[i].Source * multiplier;
}
}
public static void GenerateMult2Generic<T>(List<T> SourceList, Type ContainerType, string propNameSource, string propNameMult, int multiplier)
{
var propertyInfoSource = ContainerType.GetProperty(propNameSource);
var propertyInfoMult = ContainerType.GetProperty(propNameMult);
foreach (T item in SourceList)
{
float sourceValue = (float)propertyInfoSource.GetValue(item);
propertyInfoMult.SetValue(item, sourceValue * multiplier);
}
}
}
}
使用系统;
使用System.Collections.Generic;
命名空间TestApp
{
公开课堂记录
{
公共日期时间日期时间UTC{get;set;}
公共浮点源{get;set;}
公共浮点Mult2{get;set;}
公共浮点Mult3{get;set;}
公共浮点Mult4{get;set;}
}
班级计划
{
公共静态列表分钟数=新列表();
静态void Main(字符串[]参数)
{
对于(int i=1;i<10000000;i++)
{
分钟新分钟=新分钟();
newMinute.Source=i;
分钟。添加(新分钟);
}
GenerateMult2(分钟,2);//160毫秒
GenerateMult2Generic(分钟,类型(分钟),名称(分钟.来源),名称(分钟.Mult2),2);/4300毫秒
}
公共静态void GenerateMult2(列表分钟数,整数倍增)
{
for(int i=0;i
在这个测试应用程序中,有一个名为GenerateMult2的方法,其目的是对微小对象列表中的一个属性进行一些计算。这种方法效果好,速度快。问题是方法太具体了。如果我想对属性Mult3和Mult4进行相同的计算,我需要为每个属性创建一个单独的方法,这是太多重复的代码。我想让这个方法更通用,也就是说,我想这个方法也接受其他类型的列表,例如一个日期对象或第二个对象的列表。此外,我想告诉方法要对哪个属性执行计算
因此,我尝试创建一个名为GenerateMult2Generic的通用方法。此方法执行与GenerateMult2方法完全相同的计算,并且是多用途的,这正是我想要的。最大的缺点是它的方式太慢,由于反射
GenerateMult2方法如何以通用方式生成,但性能损失不超过5%
使用解决方案更新
在研究了这里的答案后,最好的答案是Ed Plunkett给出的,但不知何故被删除了。因此,我发布的原始代码更新了该答案中的想法:
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestApp
{
public class Minute : BaseTime
{
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class Day : BaseTime
{
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class BaseTime
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
}
class Program
{
public static List<Minute> Minutes = new List<Minute>();
public static List<Day> Days = new List<Day>();
static void Main(string[] args)
{
Minutes = Enumerable.Range(1, 10000000).Select(n => new Minute { Source = n }).ToList();
Days = Enumerable.Range(1, 10000000).Select(n => new Day { Source = n }).ToList();
// Generating data for Minutes
GenerateMovingAverage(Minutes, 100, (m, value) => ((Minute)m).MovingAverageFast = value);
GenerateMovingAverage(Minutes, 500, (m, value) => ((Minute)m).MovingAverageSlow = value);
GenerateRsi(Minutes, 60, (m, value) => ((Minute)m).RsiFast = value);
GenerateRsi(Minutes, 250, (m, value) => ((Minute)m).RsiSlow = value);
// Generating data for Days
GenerateMovingAverage(Days, 8, (d, value) => ((Day)d).MovingAverageFast = value);
GenerateMovingAverage(Days, 45, (d, value) => ((Day)d).MovingAverageSlow = value);
GenerateRsi(Days, 5, (d, value) => ((Day)d).RsiFast = value);
GenerateRsi(Days, 21, (d, value) => ((Day)d).RsiSlow = value);
}
public static void GenerateMovingAverage(IEnumerable<BaseTime> BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
float newValue;
newValue = BaseTimeObject.Source * Period; // pseudo calculation for generating moving average
setter(BaseTimeObject, newValue);
}
}
public static void GenerateRsi(IEnumerable<BaseTime> BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
float newValue;
newValue = BaseTimeObject.Source / Period; // pseudo calculation for generating rsi
setter(BaseTimeObject, newValue);
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
命名空间TestApp
{
公开课分钟:基准时间
{
公共浮点移动平均快速{get;set;}
公共浮点移动平均流{get;set;}
公共浮点数{get;set;}
公共浮点数{get;set;}
}
公众上课日:基准时间
{
公共浮点移动平均快速{get;set;}
公共浮点移动平均流{get;set;}
公共浮点数{get;set;}
公共浮点数{get;set;}
}
公共类基时
{
公共日期时间日期时间UTC{get;set;}
公共浮点源{get;set;}
}
班级计划
{
公共静态列表分钟数=新列表();
公共静态列表天数=新列表();
静态void Main(字符串[]参数)
{
Minutes=Enumerable.Range(1100000).Select(n=>newminute{Source=n}).ToList();
Days=Enumerable.Range(1100000).Select(n=>newday{Source=n}).ToList();
//生成分钟数据
生成移动平均值(分钟,100,(米,值)=>(分钟)米。移动平均值=值);
生成平均值(分钟,500,(米,值)=>(分钟)米。移动平均值=值);
生成器i(分钟,60,(米,值)=>((分钟)米)。RsiFast=值);
发电机i(分钟,250,(米,值)=>((分钟)米)。RsiSlow=值);
//生成数天的数据
生成平均值(天,8,(d,值)=>((天)d)。移动平均值=值);
生成平均值(天,45,(d,值)=>((天)d)。移动平均值=值);
发电机指数(天,5,(d,值)=>((天)d)。RsiFast=值);
发电机指数(天,21,(d,值)=>((天)d)。RsiSlow=值);
}
公共静态void GenerateMovingAverage(IEnumerable BaseTimeObjects、int句点、动作设置器)
{
foreach(BaseTimeObjects中的var BaseTimeObject)
{
浮动新值;
newValue=BaseTimeObject.Source*Period;//生成移动平均值的伪计算
setter(BaseTimeObject,newValue);
}
}
公共静态void生成器(IEnumerable BaseTimeObjects、int句点、动作设置器)
{
foreach(BaseTimeObjects中的var BaseTimeObject)
{
浮动新值;
newValue=BaseTimeObject.Source/Pe
interface ITimeMulti
{
DateTime DateTimeUtc { get; set; }
float Source { get; set; }
// will be used for number of available properties.
int MultCount { get; }
// the main method for generating the multipliers.
void Generate(int multNumber, int multiplier);
}
public class TimeMulti : ITimeMulti
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
// Using Dictionary will be much faster than Reflection
protected static Dictionary<string, float> Multipliers { get; set; }
// Number of Properties (the set should be within the derived classes)
public int MultCount { get; protected set; }
// This is a restriction to create this instance from the derived classes only
private TimeMulti() { }
// for derived classes
protected TimeMulti(int multCount)
{
// Should be in this constructor only
Initiate(multCount);
}
// This is the main method to generate the multiplication part.
public void Generate(int multNumber, int multiplier)
{
if (multNumber == 0)
{
Multipliers["Mult"] = Source * multiplier;
}
else if (Multipliers.ContainsKey("Mult" + multNumber))
{
// store the value in the dictionary (this is for reference)
Multipliers["Mult" + multNumber] = SetMult(multNumber, Source * multiplier);
}
else
{
throw new NullReferenceException();
}
}
// On new instance, this will fired, which will setup the dictionary
protected void Initiate(int numberOfMultipliers)
{
// Ensure you have an active instance of the dictionary
if (Multipliers == null)
Multipliers = new Dictionary<string, float>();
// Ensurance
if(numberOfMultipliers > 0)
{
MultCount = numberOfMultipliers;
for (int x = 1; x <= numberOfMultipliers; x++)
if (!Multipliers.ContainsKey("Mult" + x))
Multipliers.Add("Mult" + x, 0);
}
else
{
throw new ArgumentOutOfRangeException();
}
}
// this is where we will replace Reflection, here is just returning the multValue
// we will override it on the derived classes
protected virtual float SetMult(int MultNumber, float multValue) => multValue;
}
public class Minute : TimeMulti
{
public float Mult1 { get; set; }
public float Mult2 { get; set; }
public float Mult3 { get; set; }
public float Mult4 { get; set; }
// MultCount = 4
public Minute(): base(4) { }
// This method will set the value of the property using switch statment, with this, you will avoid Reflection.
protected override float SetMult(int multNumber, float multValue)
{
switch (multNumber)
{
case 1:
Mult1 = multValue;
break;
case 2:
Mult2 = multValue;
break;
case 3:
Mult3 = multValue;
break;
case 4:
Mult4 = multValue;
break;
}
return multValue;
}
}
class Program
{
// Create List with type of the ITimeMulti interface
public static List<ITimeMulti> Minutes = new List<ITimeMulti>();
static void Main(string[] args)
{
// Generate a sample
for (int i = 1; i < 10000000; i++)
Minutes.Add(new Minute() { Source = i});
// Calculate
GenerateMultipliers(Minutes, 1, 2);
}
public static void GenerateMultipliers(List<ITimeMulti> source, int multNumber, int multiplier)
{
for (int i = 0; i < source.Count; i++)
{
source[i].Generate(multNumber, multiplier);
}
}
}
public class Day : TimeMulti
{
// Properties
public float Mult1 { get; set; }
// Constructor
public Day(): base(1) { }
// This method to map the values to the properties
protected override float SetMult(int multNumber, float multValue)
{
switch (multNumber)
{
case 1:
Mult1 = multValue;
break;
}
return multValue;
}
}
public class BaseTime
{
// shared proprties
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class Minute : BaseTime
{
// add your custom code for Minute
// No need for recreating them, since it's already inherited from the base
}
public class Day : BaseTime
{
// add your custom code for Day
// No need for recreating them, since it's already inherited from the base
}
class Program
{
public static BaseTime[] Minutes;
public static BaseTime[] Days;
static void Main(string[] args)
{
Minutes = Enumerable.Range(1, 10000000).Select(n => (BaseTime) new Minute { Source = n }).ToArray();
Days = Enumerable.Range(1, 10000000).Select(n => (BaseTime) new Day { Source = n }).ToArray();
// Generating data for Minutes
GenerateMovingAverage(Minutes, 100, (m, value) => m.MovingAverageFast = value);
GenerateRsi(Minutes, 60, (m, value) => m.RsiFast = value);
GenerateRsi(Minutes, 250, (m, value) => m.RsiSlow = value);
// Generating data for Days
GenerateMovingAverage(Days, 8, (d, value) => d.MovingAverageFast = value);
GenerateMovingAverage(Days, 45, (d, value) => d.MovingAverageSlow = value);
GenerateRsi(Days, 5, (d, value) => d.RsiFast = value);
GenerateRsi(Days, 21, (d, value) => d.RsiSlow = value);
}
public static void GenerateMovingAverage(BaseTime[] BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
setter(BaseTimeObject, BaseTimeObject.Source * Period);
}
}
public static void GenerateRsi(BaseTime[] BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
setter(BaseTimeObject, BaseTimeObject.Source / Period);
}
}
}
public interface IMultiValueGenerator
{
void GenerateValue(ITimeMulti multi, int multiplier);
}
public class Multi2Generator : IMultiValueGenerator
{
public void GenerateValue(ITimeMulti multi, int multiplier)
{
multi.Mult2 = multi.Source * multiplier;
}
}
public static class MultiGeneratorFactory
{
public static IMultiValueGenerator GetGenerator(...)
{
if (condition)
return new Multi2Generator();
// etc
}
}