Генерируйте динамический метод для установки поля структуры вместо того, чтобы использовать отражение

Это не имеет значения, какое расширение Вы используете. Любой в порядке.

я использую *.h для C и *.hpp для C++.

11
задан Community 23 May 2017 в 12:26
поделиться

4 ответа

ИЗМЕНИТЬ снова : Сейчас это работает.

В C # 4 есть отличный способ сделать это, но вы перед этим придется написать свой собственный ILGenerator код emit для чего угодно. Они добавили ExpressionType.Assign в .NET Framework 4.

Это работает на C # 4 (проверено):

public delegate void ByRefStructAction(ref SomeType instance, object value);

private static ByRefStructAction BuildSetter(FieldInfo field)
{
    ParameterExpression instance = Expression.Parameter(typeof(SomeType).MakeByRefType(), "instance");
    ParameterExpression value = Expression.Parameter(typeof(object), "value");

    Expression<ByRefStructAction> expr =
        Expression.Lambda<ByRefStructAction>(
            Expression.Assign(
                Expression.Field(instance, field),
                Expression.Convert(value, field.FieldType)),
            instance,
            value);

    return expr.Compile();
}

Изменить: Вот мой тестовый код.

public struct SomeType
{
    public int member;
}

[TestMethod]
public void TestIL()
{
    FieldInfo field = typeof(SomeType).GetField("member");
    var setter = BuildSetter(field);
    SomeType instance = new SomeType();
    int value = 12;
    setter(ref instance, value);
    Assert.AreEqual(value, instance.member);
}
14
ответ дан 3 December 2019 в 02:20
поделиться

Возможно, вы захотите взглянуть на динамические методы (отражение не обязательно должно быть медленным!) ...

У Герхарда есть хорошая статья об этом: http: // jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/

1
ответ дан 3 December 2019 в 02:20
поделиться

Я столкнулся с похожей проблемой, и это заняло у меня большую часть выходных, но я, наконец, понял это после множества поисков, чтения и дизассемблирования тестовых проектов C #. И для этой версии требуется только .NET 2, а не 4.

public delegate void SetterDelegate(ref object target, object value);
private static Type[] ParamTypes = new Type[]
{
    typeof(object).MakeByRefType(), typeof(object)
};
private static SetterDelegate CreateSetMethod(MemberInfo memberInfo)
{
    Type ParamType;
    if (memberInfo is PropertyInfo)
        ParamType = ((PropertyInfo)memberInfo).PropertyType;
    else if (memberInfo is FieldInfo)
        ParamType = ((FieldInfo)memberInfo).FieldType;
    else
        throw new Exception("Can only create set methods for properties and fields.");

    DynamicMethod setter = new DynamicMethod(
        "",
        typeof(void),
        ParamTypes,
        memberInfo.ReflectedType.Module,
        true);
    ILGenerator generator = setter.GetILGenerator();
    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Ldind_Ref);

    if (memberInfo.DeclaringType.IsValueType)
    {
#if UNSAFE_IL
        generator.Emit(OpCodes.Unbox, memberInfo.DeclaringType);
#else
        generator.DeclareLocal(memberInfo.DeclaringType.MakeByRefType());
        generator.Emit(OpCodes.Unbox, memberInfo.DeclaringType);
        generator.Emit(OpCodes.Stloc_0);
        generator.Emit(OpCodes.Ldloc_0);
#endif // UNSAFE_IL
    }

    generator.Emit(OpCodes.Ldarg_1);
    if (ParamType.IsValueType)
        generator.Emit(OpCodes.Unbox_Any, ParamType);

    if (memberInfo is PropertyInfo)
        generator.Emit(OpCodes.Callvirt, ((PropertyInfo)memberInfo).GetSetMethod());
    else if (memberInfo is FieldInfo)
        generator.Emit(OpCodes.Stfld, (FieldInfo)memberInfo);

    if (memberInfo.DeclaringType.IsValueType)
    {
#if !UNSAFE_IL
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldloc_0);
        generator.Emit(OpCodes.Ldobj, memberInfo.DeclaringType);
        generator.Emit(OpCodes.Box, memberInfo.DeclaringType);
        generator.Emit(OpCodes.Stind_Ref);
#endif // UNSAFE_IL
    }
    generator.Emit(OpCodes.Ret);

    return (SetterDelegate)setter.CreateDelegate(typeof(SetterDelegate));
}

Обратите внимание на "#if UNSAFE_IL" там. На самом деле я придумал 2 способа сделать это, но первый действительно ... хакерский. Процитируем Ecma-335, документ стандартов для IL:

«В отличие от box, который требуется для создания копии типа значения для использования в объекте, unbox не требуется для копирования типа значения из объекта. Обычно он просто вычисляет адрес типа значения, который уже присутствует внутри упакованного объекта ».

Поэтому, если вы хотите играть опасно, вы можете использовать OpCodes.Unbox, чтобы изменить дескриптор объекта на указатель на вашу структуру, который затем можно использовать в качестве первого параметра Stfld или Callvirt. Выполнение этого способа фактически приводит к изменению структуры на месте, и вам даже не нужно передавать целевой объект по ссылке

. Однако обратите внимание, что стандарт не гарантирует, что Unbox предоставит вам указатель на коробочная версия. В частности, это предполагает, что Nullable <> может заставить Unbox создать копию. В любом случае, если это произойдет, вы, вероятно, получите тихий сбой, когда он устанавливает значение поля или свойства в локальной копии, которая затем немедленно отбрасывается.

Таким образом, безопасный способ сделать это - передать ваш объект по ссылке, сохранить адрес в локальной переменной, внести изменения, а затем повторно упаковать результат и вернуть его в свой параметр объекта ByRef.

Я сделал несколько приблизительных расчетов времени, вызывая каждую версию 10 000 000 раз с двумя разными структурами: .46 s "Небезопасный" делегат .70 с "Безопасный" делегат 4,5 с. FieldInfo.SetValue

Структура с 4 полями: .46 s "Небезопасный" делегат .88 s "Безопасный" делегат 4,5 с. FieldInfo.SetValue

Обратите внимание, что упаковка снижает скорость «безопасной» версии с размером структуры, тогда как на два других метода размер структуры не влияет. Думаю, в какой-то момент стоимость бокса превысит стоимость отражения. Но я бы не стал доверять «небезопасной» версии в каком-либо важном качестве.

10
ответ дан 3 December 2019 в 02:20
поделиться

Вы могли бы довольно легко модифицировать это для работы со структурами. В настоящее время он основан на словаре, но ваша ситуация проще.

http://www.damonpayne.com/2009/09/07/TwoWayBindingToNameValuePairs.aspx

0
ответ дан 3 December 2019 в 02:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: