【问题标题】:PropertyInfo.SetValue() not working but no errorsPropertyInfo.SetValue() 不工作但没有错误
【发布时间】:2012-03-14 00:44:40
【问题描述】:

我正在编写自己的方法来将对象图转换为自定义对象,因为 JavaScriptSerializer 会在 null 值上触发错误。

这就是我目前所拥有的:

internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
    T generic = (T)Activator.CreateInstance<T>();
    Type resType = typeof(T);
    foreach (PropertyInfo pi in resType.GetProperties())
    {
        object outObj = new object();
        if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
        {
            Type outType = outObj.GetType();
            if (outType == pi.PropertyType)
            {                        
                pi.SetValue(generic, outObj, null);
            }
        }
    }
    return generic;
}

现在pi.SetValue() 方法运行,并没有引发错误,但是当我查看generic 的属性时,它仍然和之前一样。

它经过的第一个属性是一个布尔值,所以值最终是这样的

generic = an object of type MyCustomType
generic.property = false
outObj = true
pi = boolean property
outType = boolean

那么SetValue方法运行后,generic.property仍然设置为false。

【问题讨论】:

  • 不相关但 generic.GetType() 在我的基准测试中会比 typeof(T) 有更好的性能。也没有理由强制转换Activator.CreateInstance&lt;T&gt; 的结果,它已经返回了T 的实例。
  • 谢谢你,我会改变那些东西。我也偶然找到了答案。
  • 你需要调试这个。 if (outType == pi.PropertyType) 没有意义,不知道你在做什么。
  • @HansPassant 检查它们是否是同一类型。

标签: c# generics reflection


【解决方案1】:

PropertyInfo.SetValue/GetValue 与 struct 一起使用,使用准确

struct Z
{
  public int X { get; set; }
}

  Z z1 = new Z();
  z1.GetType().GetProperty("X").SetValue(z1, 100, null);
  Console.WriteLine(z1.X); //z1.X dont changed

  object z2 = new Z();
  z2.GetType().GetProperty("X").SetValue(z2, 100, null);
  Console.WriteLine(((Z)z2).X); //z2.x changed to 100

  Z z3 = new Z();
  object _z3 = z3;
  _z3.GetType().GetProperty("X").SetValue(_z3, 100, null);
  z3 = (Z)_z3;
  Console.WriteLine(z3.X); //z3.x changed to 100

改变结构的正确方法:

  • 盒子结构
  • 改变盒装结构的属性
  • 将盒装结构分配给源

【讨论】:

    【解决方案2】:

    找到了答案。显然,PropertyInfo.SetValue() 和 PropertyInfo.GetValue() 不适用于结构,仅适用于类。

    不幸的是,MyCustomType 是一个结构体,因此将其更改为一个类就可以了。

    this 线程中的第三条回复说明了为什么结构不起作用而类起作用。

    编辑:它确实适用于结构,请参阅标记的答案。

    【讨论】:

    • 结构确实有效——你只需要确保拆箱结构并返回它,而不是返回原始的预先装箱的值。请参阅我的答案以了解如何做到这一点。
    【解决方案3】:

    所以我采用了你的方法并对其进行了单元测试:

    class PropertySetTest
    {
        static readonly Type resType = typeof(Car);
        internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
        {
            T generic = (T)Activator.CreateInstance<T>();
            foreach (PropertyInfo pi in resType.GetProperties())
            {
                //No need to new() this
                object outObj; // = new object();
                if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
                {
                    Type outType = outObj.GetType();
                    if (outType == pi.PropertyType)
                    {
                        pi.SetValue(generic, outObj, null);
                    }
                }
            }
            return generic;
        }
    
        [Test]
        public void Test()
        {
            var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
            var myCar = ParseObjectGraph<Car>(typeData);
            Assert.AreEqual("Blue", myCar.Color);
        }
    }
    
    internal class Car
    {
        public String Color { get; set; }
    }
    

    这通过了。你能让它不以你看到的方式通过吗?

    编辑:使用您的结构,它只会稍微复杂一些。请参阅 Jon Skeet 的回答 here 关于正在发生的事情。至于工作代码:

    class PropertySetTest
    {
        static readonly Type resType = typeof(Car);
        internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
        {
            Object generic = Activator.CreateInstance<T>();
            foreach (var pi in resType.GetProperties())
            {
                //No need to new() this
                object outObj; // = new object();
                if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
                {
                    var outType = outObj.GetType();
                    if (outType == pi.PropertyType)
                        pi.SetValue(generic, outObj, null);
                }
            }
            return (T)generic;
        }
    
        [Test]
        public void Test()
        {
            var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
            var myCar = ParseObjectGraph<Car>(typeData);
            Assert.AreEqual("Blue", myCar.Color);
        }
    }
    
    internal struct Car
    {
        public String Color { get; set; }
    }
    

    【讨论】:

    • 谢谢,通过的原因是Car 是一个类。我的对象是一个结构。
    • 是的,成功了。那么我真正改变了什么,而不是将generic 用作类型T 我只是将其用作对象?
    • 诀窍在于,当您将泛型方法与结构(它是一种值类型)一起使用时,语义会发生变化。将值类型(您的generic 变量)转换为Object(称为装箱)会在托管堆上创建该值类型的新副本。因此,在您的原始示例中,您将值类型装箱(制作副本)然后在副本上设置属性,然后返回原始(未初始化的)一个。我所做的只是将其转换为T
    • 啊啊啊啊啊我明白了。感谢您的回答和解释。
    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 2020-11-10
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 2014-12-10
    相关资源
    最近更新 更多