HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Speed problems with SetValue, ToType and Reflection

Submitted by: @import:stackexchange-codereview··
0
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

Solution

first of all reflection is expensive when used often. What you can do is

-
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>> _propertiesCache
public 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.