The goal is to create a method which generically performs calculations on a property of a list of objects in a performant manner. Below is the entire test code:
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);
}
}
}
}
In this test app there is a method called GenerateMult2, whose purpose is to make some calculation on one of the properties in a list of Minute objects. This method works fine and is fast. The problem is that the method is too specific. If I wanted to do the same calculations on the properties Mult3 and Mult4, I would need to make a separate method for each of these properties, which is too much duplicated code. I want to make this method more generic, which is, I want the method to accept lists of other types as well, for example a list of Day objects or Second objects. Furthermore, I want to tell the method which property to perform the calculations on.
So I've made an attempt at creating a generic method called GenerateMult2Generic. This method performs the exact same calculation as the GenerateMult2 method, and is multipurpose, which is what I want. The huge disadvantage is that it's way too slow due to the reflections.
How can the GenerateMult2 method be made in a generic fashion, but with a performance penalty of no more than 5%?
Update with solution
Having studied the answers here, the best is one that was given by Ed Plunkett, but somehow was removed. Therefore, I'm posting the original code updated with the ideas from that answer:
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);
}
}
}
}
The key idea here is setting the property via an Action in the caller. With this solution, the calculation methods are reused for any object and any property with good performance.
In addition to what #iSR5 wrote, you might consider using a factory design pattern, making classes that do the actual calculations. This would be good if you don't know what you actually need to do until run time.
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
}
}
Not sure if I've got the full picture here, but from my understanding, you'll need to have an interface with a base class. The interface is the one that you'll use to define the object, while the base class is the container for all common operations, which can be inhered by the class children. Then, you can create child class (as many as you want) and inherit the base class. The child class will have its required properties, methods, and logic if needed.
Enough talking, let's take it in code :
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);
}
Simple ? let's now create the base class :
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;
}
Now, the derived class
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;
}
}
Now, you can do this :
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);
}
}
}
If you want to create a new derived class :
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;
}
}
This is just an example to give you a new ideas, you can do your own magic. I wouldn't go with Mult1 ...etc. I would go with a unique and a descriptive names.
Updated :
You can improve the performance of your updated code, by gathering all common properties in the base and make use of virtual and override if you want to have something override-able in a child class. Or, use interface and struct instead of classes. Also, instead of using IEnumerable use Array this would improve your performance as well.
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);
}
}
}
I want to compare all of my list of generic T property value to my local variable searchText.
i already try:
x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
and i get error : NullReferenceException: Object reference not set to an instance of an object.
here my full method :
protected List<T> ProcessCollection<T>(List<T> lstElements, IFormCollection requestFormData, Func<T, IComparable> getProp)
{
string searchText = string.Empty;
Microsoft.Extensions.Primitives.StringValues tempOrder = new[] { "" };
if (requestFormData.TryGetValue("search[value]", out tempOrder))
{
searchText = requestFormData["search[value]"].ToString();
}
var skip = Convert.ToInt32(requestFormData["start"].ToString());
var pageSize = Convert.ToInt32(requestFormData["length"].ToString());
if (requestFormData.TryGetValue("order[0][column]", out tempOrder))
{
var columnIndex = requestFormData["order[0][column]"].ToString();
var sortDirection = requestFormData["order[0][dir]"].ToString();
tempOrder = new[] { "" };
if (requestFormData.TryGetValue($"columns[{columnIndex}][data]", out tempOrder))
{
var columName = requestFormData[$"columns[{columnIndex}][data]"].ToString();
if (pageSize > 0)
{
var prop = GetProperty<T>(columName);
if (sortDirection == "asc")
{
return lstElements.Where(x=>x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderBy(b => prop.GetValue(b)).ToList();
}
else
return lstElements
.Where(x => getProp(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderByDescending(b => prop.GetValue(b)).ToList();
}
else
return lstElements;
}
}
return null;
}
this is how i call my method :
var listItem = ProcessCollection<Jtabel>(temp,requestFormData,x=>x.Name);
the temp that i pass to method already filled
and this is the class type that i pass to this method
public class Jtabel : BaseModel
{
public string Creator { get; set; }
public string Name { get; set; }
public int SO { get; set; }
public int SOD { get; set; }
public int SAP { get; set; }
public int BA { get; set; }
public int Invoice { get; set; }
public int EPD { get; set; }
public double DP { get; set; }
}
i will try to explain more detail as much i can, so i want to get all of my property from Jtabel class to my ProcessCollection where inside the method all of Jtable property will test each element that's contain searchText.
I have this class:
public class Test
{
public string Id { get; set; }
public int Number { get; set; }
public int DoubleNumber { get; set; }
}
and a list
List<Test> myTestList;
How can I make the value of the field DoubleNumber in myTestList equal to twice the value of Number? Note that I am okay to create another list if that's needed.
If I understand your question correctly:
foreach(Test item in myList) {
item.DoubleNumber = 2*item.Number;
}
Or, if it's ok, just remove the setter and modify the getter to return 2x Number:
public class Test
{
public string Id { get; set; }
public int Number { get; set; }
public int DoubleNumber { get { return 2* this.Number; } } //completely remove setter
}
Or, if you still want to be able to modify DoubleNumber:
public class Test {
private int m_num;
private int m_doubleNum;
public string Id {
get;
set;
}
public int Number {
get {
return this.m_num;
}
set {
this.m_num = value;
this.m_doubleNum = 2 * value; //when Number is set, update m_doubleNum too
}
}
public int DoubleNumber {
get {
return this.m_doubleNum;
}
set {
this.m_doubleNum = value; //allow manual setting of DoubleNumber
//or maybe also modify Number here?
//this.m_num = value / 2;
}
}
}
One way it could be using a foreach statement:
foreach(var item in myTestList)
{
item.DoubleNumber = 2*item.Number;
}
Another way it could be to use LINQ.
var result = myTestList.Select(test => new Test
{
test.Id,
test.Number,
DoubleNumber = 2*test.Number;
})
.ToList();
Among the two ways I would prefer the first one, since it's more clear what you are trying to do and more performant (in the second approach you have to create a new object for each object in myTestList).
I need to run validation on JSON input model by taking the value from one List and check if it exists in another. This value is a rating is from 1 - 5. If there is no matching number, then it should throw an error. Below is the code and this logic goes in the section commented with : //check if rating exist in score table
namespace Alpha.Model
{
// INPUT
public class AlphaCalcParamMethod
{
public ICollection<PortfolioInputModel> portfolios { get; set; }
public Setting settings { get; set; }
public bool Validation(ref string errString)
{
// Check if portfolio exists
if(portfolios == null || portfolios.Count < 1)
{
errString = "At least 1 Portfolio.";
return false;
}
//check if weight adds upto 1
foreach(var portfolio in portfolios)
{
// var holdings = new List<PortfolioHoldingInput>();
var weightAggregator = 0.00;
foreach(var holding in portfolio.portfolioHoldings)
{
weightAggregator += holding.fundWeight;
}
if (weightAggregator != 1)
{
errString = "Fund Weights should add upto 1";
}
return false;
}
//check if rating exist in score table
foreach(var portfolio in portfolios)
{
var holdings = new List<PortfolioHoldingInput>();
var scores = new List<Setting>();
foreach(var holding in holdings)
{
//fetch the value of fundRating double
foreach(var score in scores)
{
//check if the value above exist in grossAlpha's List fundRating
// if it doesn't return false
}
}
return false;
}
return true;
}
}
// OUTPUT
public class AlphaCalcResultMethod
{
public List<PortfolioOutputModel> portfolios { get; set; }
}
public class PortfolioInputModel
{
public string portfolioIdentifier { get; set; }
public ICollection<PortfolioHoldingInput> portfolioHoldings { get; set; }
}
public class PortfolioOutputModel
{
public string portfolioIdentifier { get; set; }
public double portfolioAlpha { get; set; }
public ICollection<PortfolioHoldingOutput> portfolioHoldings { get; set; }
}
public class PortfolioHoldingInput
{
public string fundIdentifier { get; set; }
public int fundRating { get; set; }
public double fundExpenseRatio { get; set; }
public double fundWeight { get; set; }
}
public class PortfolioHoldingOutput
{
public string fundIdentifier { get; set; }
public int fundRating { get; set; }
public double fundExpenseRatio { get; set; }
public double fundWeight { get; set; }
public double fundAlpha { get; set; }
}
public class Setting
{
public List<GrossAlpha> grossAlphas { get; set; }
}
public class GrossAlpha
{
public int fundRating { get; set; }
public double grossAlpha { get; set; }
}
}
If you are going to return additional values from method, you should use out parameters.
Don't specify type of variable in variable name. I.e. instead of errorString just use error. Hungarian notation and other tricks are not needed with modern IDEs.
Double type is not precise. You should avoid comparing it to integer values for equality. Prefer greater or less than comparisons.
Use LINQ to replace loops
Method name Validation is a noun. That is pretty confusing for method which is action and should a verb. Consider rename it to Validate or IsValid
Code
public bool IsValid(out string error)
{
if (portfolios?.Any() == false)
{
error = "At least 1 Portfolio.";
return false;
}
if (portfolios.Any(p => p.portfolioHoldings.Sum(h => h.fundWeight) < 1))
{
error = "Fund Weights should add upto 1";
return false;
}
var holdings = portfolios.SelectMany(p => p.portfolioHoldings);
var validRatings = new List<int> { 1, 2, 3, 4, 5 };
if (holdings.Any(h => !validRatings.Contains(h.fundRating)))
{
error = "Fund ratings should be in " + String.Join(",", validRatings);
return false;
}
error = "";
return true;
}
Note: if valid ratings are sequential numbers, then you can just check range of fundRating value:
if (holdings.Any(h => h.fundRating < 1 || 5 < h.fundRating))
Look this code:
if(!score.grossAlphas.Exists(x => x.fundRating == holding.fundRating))
{
return false;
}
it check if exists grossAlphas with fundRating equals holding.fundRating.
put it in the loop where you want to check, let me know if it is what you want and if it works.
I have the following class properties:
[EffectAspect(Enums.Effects.Low)]
public int Wind { set; get; }
[EffectAspect(Enums.Effects.Low)]
public int Fire { set; get; }
[EffectAspect(Enums.Effects.Medium)]
public int Water { get; set; }
[EffectAspect(Enums.Effects.Huge)]
public int Earth { get; set; }`
Now, let's say I want to calculate the total Lows, Mediums and Huges.
So I wrote something like:
List<Enums.Effects> result = new List<Enums.Effects>();
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
object[] attrs = p.GetCustomAttributes(true);
foreach (Object attr in attrs)
{
var effectAttr = attr as EffectAspect;
if (effectAttr != null)
{
int amount = (int)p.GetConstantValue();
for (int i = 0; i < amount; i++)
{
result.Add(effectAttr.Aspect);
}
}
}
}
return result;
For example: if Wind = 3, there would be at least 3 Enums.Effects.Low inside the result list.
[AttributeUsage(AttributeTargets.Property)]
public sealed class EffectAspectAttribute : Attribute
{
public Enums.EffectsAspect { get; private set; }
public EffectAspectAttribute (Enums.EffectsAspect aspect)
{
this.Aspect = aspect;
}
}
The problems is: int amount = (int)p.GetConstantValue(); throws exceptions says:
Literal value was not found.
And I couldn't find what it means.
You can try to use
p.GetValue(this, null)
instead of
p.GetConstantValue();
You can refer this link thread: Difference between GetValue, GetConstantValue and GetRawConstantValue