2

PropertyInfo指定された に指定されたの値を設定する次のメソッドがありますTInstance。これは、反射の非効率性を回避するためです。

public static Action<TInstance, object> CreateSetter<TInstance>(PropertyInfo propertyInfo, bool includeNonPublic = false)
{
    var setMethod = propertyInfo.GetSetMethod(includeNonPublic);

    var instance = Expression.Parameter(typeof(TInstance), "instance");
    var value = Expression.Parameter(typeof(object), "value");
    var valueCast = !propertyInfo.PropertyType.IsValueType
        ? Expression.TypeAs(value, propertyInfo.PropertyType)
        : Expression.Convert(value, propertyInfo.PropertyType);

    return Expression.Lambda<Action<TInstance, object>>(
        Expression.Call(instance, setMethod, valueCast), instance, value).Compile();
}

したがって、次のモデルが与えられます。

public sealed class PersonClass
{
    public string Name {get; set;}    
}

私はName使用して設定することができます:

var person = new PersonClass(); 
var nameProp = person.GetType().GetProperties().Where(p => p.Name == "Name").First();
var nameSetter = CreateSetter<PersonClass>(nameProp);
nameSetter(person, "Foo");

これはすべて問題ありませんが、structたとえば次の方法を試してみると:

public struct PersonStruct
{
    public string Name {get; set;}    
}

名前は常にnull. ボックス化/ボックス化解除がどういうわけか私を噛んでいると思います.

実際、使用FastMember時に同じ動作が発生した場合:

PersonStruct person = new PersonStruct();   
var accessor = TypeAccessor.Create(person.GetType());       
accessor[person, "Name"] = "Foo";

ただし、personasobjectをボックス化するとFastMember、値を正しく設定できます。

object person = new PersonStruct(); 
var accessor = TypeAccessor.Create(person.GetType());       
accessor[person, "Name"] = "Foo";

値型のCreateSetter場合にこのボクシングを処理する方法はありますか?TInstance

4

2 に答える 2

1

コピーではなく、渡された構造体に影響を与えるように、参照渡しの引数を取るデリゲートを作成する式が必要です。例えば:

public struct PersonStruct
{
    public string Name {get; set;}    
}

delegate void FirstByRefAction<T1, T2>(ref T1 arg1, T2 arg2);

void Main()
{
    ParameterExpression par1 = Expression.Parameter(typeof(PersonStruct).MakeByRefType());
    ParameterExpression par2 = Expression.Parameter(typeof(string));
    FirstByRefAction<PersonStruct, string> setter = Expression.Lambda<FirstByRefAction<PersonStruct, string>>(
        Expression.Assign(Expression.Property(par1, "Name"), par2),
        par1, par2
        ).Compile();
    PersonStruct testStruct = new PersonStruct();
    setter(ref testStruct, "Test Name");
    Console.Write(testStruct.Name); // outputs "Test Name"
}

これは、反射の非効率性を回避するためです。

メソッドとプロパティを呼び出す式の型は、主に内部でリフレクションを使用することに注意してください。解釈された式の場合、それらは各呼び出しで行われます。IL でコンパイルされた式の場合、リフレクションはコンパイル ステップで引き続き使用されます。

于 2017-01-10T17:29:49.097 に答える
1

コメントに記載されているように、変更可能な構造体を作成するべきではありません。ただし、質問に答えると、構造体は値型であるため、構造体のコピーがに渡されるActionため、元の人の値は変更されません。

参照渡しする方法が必要ですstructただし、式は、参照によってパラメータを取る「メソッド」の作成をサポートしていません。

できることは、DynamicMethodクラスを使用して次のようなことを行うことです。

public delegate void StructSetter<TInstance>(ref TInstance instance, object value) where TInstance : struct;

public StructSetter<TInstance> CreateSetter<TInstance>(
    PropertyInfo propertyInfo,
    bool includeNonPublic = false) where TInstance : struct
{
    DynamicMethod method =
        new DynamicMethod(
            "Set",
            typeof(void),
            new [] { typeof(TInstance).MakeByRefType(), typeof(object )},
            this.GetType());

    var generator = method.GetILGenerator();

    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Ldarg_1);
    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(includeNonPublic));
    generator.Emit(OpCodes.Ret);

    return (StructSetter<TInstance>)method.CreateDelegate(typeof (StructSetter<TInstance> ));
}

StructSetter標準のActionデリゲートは参照渡しをサポートしていないため、デリゲートを作成する必要がありました。

デリゲートをキャッシュすることを忘れないでください。そうしないと、コンパイルのコストによってアプリケーションの速度が低下します。

于 2017-01-10T17:19:05.720 に答える