【问题标题】:Strange Reflection problem - can't get it奇怪的反射问题 - 无法得到它
【发布时间】:2011-01-12 12:48:09
【问题描述】:

请帮助我了解这里发生了什么以及它是否应该这样工作? 我有一个来自 CMS 的通用对象列表:

例如List<MyCMS.Articles.Article> myArticles = articles.All;

稍后我以 JSON 格式输出列表的内容(用于 CMS UI - 表格列表)。

现在一条记录将包括:

article.Title
article.Alias
article.Guid
article.Description
+
article.SeoProperties.TitleOverride
article.SeoProperties.H1Tag
article.StateProperties.IsActive
article.StateProperties.Channels

等等……

如您所见,Article 对象具有额外的类属性 - 具有通用属性(用于 CMS 中的其他对象类型)

我还使用了一个过滤器类,它使用 LINQ 对集合执行一些过滤操作,以仅返回某个频道内的文章,例如...

所以问题是,当我将集合序列化为 JSON 时 - 我真正需要在表格列表中显示的“列”只有几个“列”,而我在其他字段中不需要 - 尤其是可能很长的字段,例如作为“描述”(从文件系统延迟加载)等...... - 我用 DataContractJsonSerializer 序列化......

我需要一种方法来控制 JSON 结果中将包含哪些字段...如果我不需要该属性,我会使用反射将属性值设置为 null 并且 用 [DataMember(IsRequired = false, EmitDefaultValue = false)] 属性装饰类属性... - 它应该工作得很好 - 但是 - 一旦我检查(甚至克隆!!)最终对象的集合以剥离字段 =将值设置为“null” - 属性值变为 null - 应用程序范围 - 在此类对象的所有集合中......嗯?

这里有一些演示代码:

void Page_Load() {
        MyCms.Content.Games games = new MyCms.Content.Games();
        List<MyCms.Content.Games.Game> allGames = games.All;

        MyCms.Content.Games games2 = new MyCms.Content.Games();
        List<MyCms.Content.Games.Game> allGamesOther = games2.All;

        Response.Write("Total games: " + allGames.Count + "<br />");

        //This is our fields stripper - with result assigned to a new list
        List<MyCms.Content.Games.Game> completelyUnrelatedOtherIsolated_but_notSureList = RemoveUnusedFields(allGamesOther);

        List<MyCms.Content.Games.Game> gamesFiltered = allGames.Where(g=>g.GamingProperties.Software=="89070ef9-e115-4907-9996-6421e6013993").ToList();

        Response.Write("Filtered games: " + gamesFiltered.Count + "<br /><br />");

    }

    private List<MyCms.Content.Games.Game> RemoveUnusedFields(List<MyCms.Content.Games.Game> games)
    {
        List<MyCms.Content.Games.Game> result = new List<MyCms.Content.Games.Game>();

        if (games != null && games.Count > 0)
        {
            //Retrieve a list of current object properties
            List<string> myContentProperties = MyCms.Utils.GetContentProperties(games[0]);

            MyCms.PropertyReflector pF = new MyCms.PropertyReflector();

            foreach (MyCms.Content.Games.Game contentItem in games)
            {
                MyCms.Content.Games.Game myNewGame = (MyCms.Content.Games.Game)contentItem.Clone();
                myNewGame.Images = "wtf!"; //just to be sure we do set this stuff not only null

                pF.SetValue(myNewGame, "GamingProperties.Software", ""); //set one property to null for testing

                result.Add(myNewGame);

            }
        }

    return result;
}

对象被设置为它们的“默认值”(基本上,在大多数情况下为空):

 private object GetDefaultValue(Type type)
        {
            if (type.IsValueType)
            {
                try
                {
                    return Activator.CreateInstance(type);
                }
                catch {
                    return null;
                }
            }

            return null;
        }

【问题讨论】:

  • 如果我没记错的话,基本上,你将一个类的属性设置为null,而其他类的相同属性变为null。你能发布一些代码吗?也许这里有:“我所做的是使用反射将属性值设置为空。”一定有某种东西将这些实例相互联系起来。 MyCms.Article.Article 对象是您设计的吗?
  • 是的,我设计了这个类,完全控制了它,我没有在任何地方使用任何静态字段......我非常同意你的怀疑,就像属性正在设置在整个班级类型元数据应用程序范围内......(稍后将发布一些代码)
  • Game.Clone()的实现是什么?
  • 能否详细介绍 MyCms.PropertyReflector SetValue 方法?
  • 在 Game 类中实现一个 ICloneable 接口,使用 public object Clone() { return this.MemberwiseClone(); }

标签: c# json reflection content-management-system setvalue


【解决方案1】:

很可能您在区分浅拷贝和深拷贝时遇到了麻烦。

如果字段是值类型,则执行该字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其克隆引用同一个对象

当您克隆一个对象并且该对象具有引用类型的字段时,使用深层副本,该对象的新克隆被创建并分配给该字段(与仅引用第一个对象相比)。因此,您必须完全不同的对象不共享任何内容。

这意味着如果您使用克隆并且某些属性实际上是子属性(即原始对象内实例的属性),您将在应用程序范围内更改它,因为您正在根据引用进行操作,不是子对象的新实例。

你有更多关于它的信息

http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

【讨论】:

  • 似乎就是这样 - 特别是我的问题与本身就是类的子属性有关。我只需要查看应用深层副本的最佳选项(通过将对象序列化为 XML 的示例,我分心于“深层复制”我的对象...... :))但我觉得关键就在这里......谢谢你!我将更新和优化我的代码,并希望(猜测,肯定)将其标记为答案。
  • 但是我什至必须克隆它吗?我的意思是 - 克隆是我试图以某种方式摆脱在应用程序范围内设置属性的尝试......但我再次理解您的回答,顺便说一句,非常感谢您提供与此问题描述的精确链接。
【解决方案2】:

您可以创建一种具有必要字段的模型视图类,并使用 Automapper 之类的东西来填充它们。通过这种方式,您将拥有一个漂亮的代码,易于维护且高度可定制。

【讨论】:

  • 亲爱的 Tiago,感谢您的回答,但问题是 - 虽然我可以尝试通过各种解决方法来解决它 - 我只需要了解(否则我怎么能声称自己是一个没有完全理解该主题:) 为什么在保存此对象类型的完全不相关的变量中将属性设置为 null...
  • 它应该是这样工作的——我有一个表单中需要的已知字段列表: private string[] RequiredFields = { "Guid", "Title", "GamingProperties.UrlDirectGame" , "StateProperties.IsActive", "别名" };
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-19
相关资源
最近更新 更多