patterncsharpMinor
Speed problems with SetValue, ToType and Reflection
Viewed 0 times
reflectionsetvaluewithtotypeandproblemsspeed
Problem
I have a 'speed problem' with a program im currently developing. I already have encountered the methods which costs some time.
```
public void ConvertDataFrameToValueFields()
{
List items = new List() { new byte[2], new byte[2], new byte[2], new byte[2] };
items[0][0] = this.DataBytes[0];
items[0][1] = this.DataBytes[1];
items[1][0] = this.DataBytes[2];
items[1][1] = this.DataBytes[3];
items[2][0] = this.DataBytes[4];
items[2][1] = this.DataBytes[5];
items[3][0] = this.DataBytes[6];
items[3][1] = this.DataBytes[7];
int i = 0;
IEnumerable result = this.determineCanMessageValueFields();
foreach (var prop in result)
{
if (prop.PropertyType == typeof(MessageStatusValueField))
{
prop.SetValue(this, new MessageStatusValueField(BitConverter.ToUInt16(items[i], 0), prop.Name, this.UnitType), null);
}
else if (prop.PropertyType == typeof(MessageValueField))
{
prop.SetValue(this, new MessageValueField(BitConverter.ToUInt16(items[i], 0), prop.Name), null);
}
else if (prop.PropertyType == typeof(MessageValueFieldInt32))
{
prop.SetValue(this, new MessageValueFieldInt32(BitConverter.ToUInt16(items[i], 0), prop.Name), null);
}
else if (prop.PropertyType.GetCustomAttributes(false).Count(x => x.GetType() == typeof(BitmaskValueFieldClass)) > 0)
{
var tempField = new MessageValueField(BitConverter.ToUInt16(items[i], 0), prop.Name);
prop.SetValue(this, Convert.ChangeType(tempField, prop.PropertyType), null);
}
i++;
}
}
private IEnumerable determineCanMessageValueFields()
{
//Get all properties with the
//custom attribute [IsValueField]
return this.GetType().GetPropert
```
public void ConvertDataFrameToValueFields()
{
List items = new List() { new byte[2], new byte[2], new byte[2], new byte[2] };
items[0][0] = this.DataBytes[0];
items[0][1] = this.DataBytes[1];
items[1][0] = this.DataBytes[2];
items[1][1] = this.DataBytes[3];
items[2][0] = this.DataBytes[4];
items[2][1] = this.DataBytes[5];
items[3][0] = this.DataBytes[6];
items[3][1] = this.DataBytes[7];
int i = 0;
IEnumerable result = this.determineCanMessageValueFields();
foreach (var prop in result)
{
if (prop.PropertyType == typeof(MessageStatusValueField))
{
prop.SetValue(this, new MessageStatusValueField(BitConverter.ToUInt16(items[i], 0), prop.Name, this.UnitType), null);
}
else if (prop.PropertyType == typeof(MessageValueField))
{
prop.SetValue(this, new MessageValueField(BitConverter.ToUInt16(items[i], 0), prop.Name), null);
}
else if (prop.PropertyType == typeof(MessageValueFieldInt32))
{
prop.SetValue(this, new MessageValueFieldInt32(BitConverter.ToUInt16(items[i], 0), prop.Name), null);
}
else if (prop.PropertyType.GetCustomAttributes(false).Count(x => x.GetType() == typeof(BitmaskValueFieldClass)) > 0)
{
var tempField = new MessageValueField(BitConverter.ToUInt16(items[i], 0), prop.Name);
prop.SetValue(this, Convert.ChangeType(tempField, prop.PropertyType), null);
}
i++;
}
}
private IEnumerable determineCanMessageValueFields()
{
//Get all properties with the
//custom attribute [IsValueField]
return this.GetType().GetPropert
Solution
first of all reflection is expensive when used often. What you can do is
-
cache the properties to set
-
remove the copying over by creating the result immediatly and let it parse it itself
-
limit the calls to GetCustomAttributes by using the result twice
-
remove the conversion of byte[] to List because it is not needed. Convert takes the startindex anyway
and together it will be
remember to hold one global instance of this class or make everything static
you have not many Message classes and convert a lot you can also create Delegates to set the properties.
create to cache it
and use it
-
cache the properties to set
Dictionary> _propertiesCache-
remove the copying over by creating the result immediatly and let it parse it itself
public TMessage ConvertDataFrameToValueFields(byte[] dataBytes, Whatever unitType) where TMessage : new()
{
var result = new TMessage();
...
// use the constructor which takes a ushort and the propertyName. The class itself has to know how to interpret the ushort
fieldValue = Activator.CreateInstance(prop.PropertyType, value, prop.Name);
...
}-
limit the calls to GetCustomAttributes by using the result twice
var isValueField = prop.GetCustomAttributes(false).OfType().FirstOrDefault();
if (isValueField != null)
results.Add(isValueField.FieldIndex, prop);-
remove the conversion of byte[] to List because it is not needed. Convert takes the startindex anyway
- remove the need to store the bytes in the class
and together it will be
class ByteConverter
{
private readonly Dictionary> _propertiesCache = new Dictionary>();
public TMessage ConvertDataFrameToValueFields(byte[] dataBytes, Whatever unitType) where TMessage : new()
{
var result = new TMessage();
int i = 0;
foreach (var prop in GetMessageValueFields())
{
var value = BitConverter.ToUInt16(dataBytes, i);
object fieldValue = null;
if (prop.PropertyType == typeof(MessageStatusValueField))
{
fieldValue = new MessageStatusValueField(value, prop.Name, this.UnitType);
}
else if (prop.PropertyType == typeof(MessageValueField))
{
fieldValue = new MessageValueField(value, prop.Name);
}
else if (prop.PropertyType == typeof(MessageValueFieldInt32))
{
fieldValue = new MessageValueFieldInt32(value, prop.Name);
}
else if (prop.PropertyType.IsSubclassOf(typeof(BitmaskValueFieldClass)))
{
// use the constructor which takes a ushort and the propertyName. The class itself has to know how to interpret the ushort
fieldValue = Activator.CreateInstance(prop.PropertyType, value, prop.Name);
}
if (fieldValue != null)
prop.SetValue(result, fieldValue, null);
i += 2;
}
return result;
}
private IEnumerable GetMessageValueFields()
{
var type = typeof(TMessage);
IEnumerable properties;
if (!_propertiesCache.TryGetValue(type, out properties))
{
//Get all properties with the
//custom attribute [IsValueField]
var results = new SortedList();
foreach (var prop in type.GetProperties())
{
var isValueField = prop.GetCustomAttributes(false).OfType().FirstOrDefault();
if (isValueField != null)
results.Add(isValueField.FieldIndex, prop);
}
properties = results.Values;
_propertiesCache.Add(type, properties);
}
return properties;
}
}remember to hold one global instance of this class or make everything static
you have not many Message classes and convert a lot you can also create Delegates to set the properties.
create to cache it
var message = Expression.Parameter(messageType, "message");
var value = Expression.Parameter(prop.PropertyType, "value");
var body = Expression.Assign(Expression.Property(message, prop), value);
var setProperty = Expression.Lambda(body, message, value).Compile();and use it
setProperty.DynamicInvoke(message, fieldValue);Code Snippets
Dictionary<Type, IEnumerable<PropertyInfo>> _propertiesCachepublic TMessage ConvertDataFrameToValueFields<TMessage>(byte[] dataBytes, Whatever unitType) where TMessage : new()
{
var result = new TMessage();
...
// use the constructor which takes a ushort and the propertyName. The class itself has to know how to interpret the ushort
fieldValue = Activator.CreateInstance(prop.PropertyType, value, prop.Name);
...
}var isValueField = prop.GetCustomAttributes(false).OfType<IsValueFieldAttribute>().FirstOrDefault();
if (isValueField != null)
results.Add(isValueField.FieldIndex, prop);class ByteConverter
{
private readonly Dictionary<Type, IEnumerable<PropertyInfo>> _propertiesCache = new Dictionary<Type, IEnumerable<PropertyInfo>>();
public TMessage ConvertDataFrameToValueFields<TMessage>(byte[] dataBytes, Whatever unitType) where TMessage : new()
{
var result = new TMessage();
int i = 0;
foreach (var prop in GetMessageValueFields<TMessage>())
{
var value = BitConverter.ToUInt16(dataBytes, i);
object fieldValue = null;
if (prop.PropertyType == typeof(MessageStatusValueField))
{
fieldValue = new MessageStatusValueField(value, prop.Name, this.UnitType);
}
else if (prop.PropertyType == typeof(MessageValueField))
{
fieldValue = new MessageValueField(value, prop.Name);
}
else if (prop.PropertyType == typeof(MessageValueFieldInt32))
{
fieldValue = new MessageValueFieldInt32(value, prop.Name);
}
else if (prop.PropertyType.IsSubclassOf(typeof(BitmaskValueFieldClass)))
{
// use the constructor which takes a ushort and the propertyName. The class itself has to know how to interpret the ushort
fieldValue = Activator.CreateInstance(prop.PropertyType, value, prop.Name);
}
if (fieldValue != null)
prop.SetValue(result, fieldValue, null);
i += 2;
}
return result;
}
private IEnumerable<PropertyInfo> GetMessageValueFields<TMessage>()
{
var type = typeof(TMessage);
IEnumerable<PropertyInfo> properties;
if (!_propertiesCache.TryGetValue(type, out properties))
{
//Get all properties with the
//custom attribute [IsValueField]
var results = new SortedList<int, PropertyInfo>();
foreach (var prop in type.GetProperties())
{
var isValueField = prop.GetCustomAttributes(false).OfType<IsValueFieldAttribute>().FirstOrDefault();
if (isValueField != null)
results.Add(isValueField.FieldIndex, prop);
}
properties = results.Values;
_propertiesCache.Add(type, properties);
}
return properties;
}
}var message = Expression.Parameter(messageType, "message");
var value = Expression.Parameter(prop.PropertyType, "value");
var body = Expression.Assign(Expression.Property(message, prop), value);
var setProperty = Expression.Lambda(body, message, value).Compile();Context
StackExchange Code Review Q#49346, answer score: 2
Revisions (0)
No revisions yet.