5

昨日ここで、無名オブジェクトからプロパティを読み取り、クラスのプライベート フィールドに書き込むことについて質問しました。問題は解決しました。ここに短い話があります:

json形式のデータがあります。それらを にデシリアライズし、メソッドExpandoObjectとして渡しますIDictionary<string, object>。プロパティを除いて、正常に動作しInt32ます。に変わっているようですがInt64、どこですか?知らない。

方法は次のとおりです。

    private Func<IDictionary<string, object>, dynamic> MakeCreator(
        Type type, Expression ctor,
        IEnumerable<PropertyToFieldMapper> maps) {

        var list = new List<Expression>();
        var vList = new List<ParameterExpression>();

        // creating new target
        var targetVariable = Expression.Variable(type, "targetVariable");
        vList.Add(targetVariable);
        list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));

        // accessing source
        var sourceType = typeof(IDictionary<string, object>);
        var sourceParameter = Expression.Parameter(sourceType, "sourceParameter");

        // calling source ContainsKey(string) method
        var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) });

        var accessSourceIndexerProp = sourceType.GetProperty("Item");
        var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod();

        // itrate over writers and add their Call to block
        var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument");
        vList.Add(containsKeyMethodArgument);
        foreach (var map in maps) {
            list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name)));
            var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo,
                                                        new Expression[] { containsKeyMethodArgument });

            // creating writer
            var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo,
                                              new Expression[] { containsKeyMethodArgument });
            var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
            var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo,
                new Expression[] {
                                     Expression.Convert(targetVariable, typeof(object)),
                                     Expression.Convert(sourceValue, typeof(object))
                                 });
            Console.WriteLine(Expression.Lambda(setterCall));
            list.Add(Expression.IfThen(containsKeyMethodCall, setterCall));
        }
        list.Add(targetVariable);

        var block = Expression.Block(vList, list);

        var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(
            block, new[] { sourceParameter }
            );

        return lambda.Compile();
    }

これがあれば

public class Person {
    public int Age { get; set; }
    public string Name { get; set; }
}

クラス、およびこのオブジェクトを使用します

var data = new { Name = "Amiry", Age = 20 };

Person上記のメソッドを使用してインスタンスを初期化すると、次のエラーが発生します。

タイプ 'System.Int64' のオブジェクトは、タイプ 'System.Int32' に変換できません。

しかし、Ageプロパティを次のように変更すると:

public long Age { get; set; }

すべてがうまく見え、メソッドは完全に機能します。なぜこれが起こるのか、私は完全に混乱しました。何か考えはありますか?

4

2 に答える 2

3

したがって、入力には次Dictionaryが含まれますlong(コメントでの議論に基づく)。

最も簡単な修正は、Convert.ChangeType前に追加することSetValueです。( and
を渡す)ただし、これにより、変換 が許可されるという意図しない結果が生じる可能性があります。sourceValueConstant(map.Field.FieldType)
string -> int

代わりにConvertType、型の変換方法を決定する独自のメソッドを追加することもできます。

于 2013-05-22T05:35:01.193 に答える