奇怪的反射问题 - 无法得到它
请帮助我了解这里发生了什么以及它是否应该像那样工作? 我有一个来自 CMS 的通用对象列表:
例如 List
;
后来我以 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;
}
对象被设置为其“默认值”(在大多数情况下基本上为 null):
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
try
{
return Activator.CreateInstance(type);
}
catch {
return null;
}
}
return null;
}
please help me understand what's going on in here and whether it should work like that ?
I have a generic list of objects from a CMS:
For example List<MyCMS.Articles.Article> myArticles = articles.All
;
Later I output the contents of the list in a JSON format (for CMS UI - table list).
Now a single record would include:
article.Title
article.Alias
article.Guid
article.Description
+
article.SeoProperties.TitleOverride
article.SeoProperties.H1Tag
article.StateProperties.IsActive
article.StateProperties.Channels
etc...
as you can see an Article object has an additional class property - with common properties (used on other object types in the CMS)
I also use a filter class that does some filter operations with LINQ on the collection to return me only articles within a certain channel, for example...
So the problem is that when I serialize the collection as JSON - there are only a few "columns" that I really need to display in my table list, while I have no need in other fields - especially, possibly long fields such as "description" (lazy loaded from file system), etc... - I serialize with DataContractJsonSerializer...
I need a way to control what fields will be included in the JSON result... What I do is I use reflection to set property values to null if I don't need the property and
decorate class properties with [DataMember(IsRequired = false, EmitDefaultValue = false)] attributes... - it should work well - but - as soon as I go over (even cloned!!) collection of final objects to strip off the fields = set value to "null" - property value becomes null - application wide - in all collections of such objects... eh ?
Some demo code in here:
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;
}
Objects are set to their "Default values" (basically, null, in most cases) with this:
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
try
{
return Activator.CreateInstance(type);
}
catch {
return null;
}
}
return null;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您很可能无法区分浅复制和深复制。
通过深层复制,当您克隆一个对象并且该对象具有引用类型的字段时,将创建该对象的新克隆并将其分配给该字段(而不是仅引用第一个对象)。因此,您必须使用完全不同的、不共享任何内容的对象。
这意味着如果您使用克隆并且某些属性实际上是子属性(即原始对象内实例的属性),那么您将在应用程序范围内更改它,因为您正在对引用而不是新对象进行操作子对象的实例。
有关详细信息,请访问
http://msdn.microsoft。 com/en-us/library/system.object.memberwiseclone.aspx
Quite probably your having trouble with differentiating between a shallow copy and a deep copy.
With a deep copy when you clone an object and that object has a field of reference type, a new clone of that object is created and assigned to the field (vs just referencing the first object). Thus you have to completely different objects that share nothing.
That means if you're using clone and some of the properties are in fact subproperties (that is, properties of an instance inside the original object) you're changing it application wide, because you're acting upon a reference, not a new instance of the subobject.
You have more information about it in
http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
您可以创建一种具有必要字段的模型视图类,并使用 Automapper 之类的东西来填充它们。通过这种方式,您将拥有一个漂亮的代码,易于维护且高度可定制。
You could create a kind of modelview classes with the necessary fields and use something like Automapper to fill them. By this way, you will have a nice code, easy to maintain and highly customizable.