{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/clrmd/issues/24","labels_url":"https://api.github.com/repos/Microsoft/clrmd/issues/24/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/clrmd/issues/24/comments","events_url":"https://api.github.com/repos/Microsoft/clrmd/issues/24/events","html_url":"https://github.com/Microsoft/clrmd/issues/24","id":110351410,"number":24,"title":"Object and field representation (dynamic and otherwise)","user":{"login":"leculver","id":8496639,"avatar_url":"https://avatars.githubusercontent.com/u/8496639?v=3","gravatar_id":"","url":"https://api.github.com/users/leculver","html_url":"https://github.com/leculver","followers_url":"https://api.github.com/users/leculver/followers","following_url":"https://api.github.com/users/leculver/following{/other_user}","gists_url":"https://api.github.com/users/leculver/gists{/gist_id}","starred_url":"https://api.github.com/users/leculver/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leculver/subscriptions","organizations_url":"https://api.github.com/users/leculver/orgs","repos_url":"https://api.github.com/users/leculver/repos","events_url":"https://api.github.com/users/leculver/events{/privacy}","received_events_url":"https://api.github.com/users/leculver/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/clrmd/labels/enhancement","name":"enhancement","color":"84b6eb"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2015-10-08T00:53:58Z","updated_at":"2015-10-08T05:37:12Z","closed_at":null,"body":"ClrMD should provide a more sensible way to access fields, values, and so on.  Instead of relying on \"object GetValue\".  Here is some of the previous discussion (edited for brevity and clarity).  I will refrain from putting my opinion here.\r\n\r\nThere are several primary topics of discussion for this design:\r\n\r\n1. Interior flag.\r\n2. Getting a single field/object value.\r\n3. Getting a chain of values.\r\n4.  Enumeration array contents.\r\n5.  Enumeration of advanced containers. (List/Dictionary/etc.)\r\n6.  Converting more complex structures such as DateTime, Guid, etc.\r\n\r\n@Zvirja starts:\r\n\r\nI analyzed the way with the `dynamic` keyword and liked it (even added to todo), however decided to proceed with a \"static\" direction. I defined the `IPtr` interface that provides address, type and heap and build a set of different extension methods around that. One kind of extensions allows to read fields from the object.\r\nBelow are examples of code that is used to achieve the different tasks.\r\n\r\nRead fields:\r\n\r\n    IPtr ptr; //here we somehow get the object.\r\n    int intValue = ptr.GetInt(\"intField\", /*interior true/false*/)\r\n    string strValue = ptr.GetString(\"stringField\");\r\n    IPtr refValue = ptr.GetRef(\"ptrField\");\r\n    IPtr structPtr = ptr.GetStruct(\"structField\");\r\n\r\n\r\nRead chains:\r\n\r\n    int intValue = ptr.GetSimpleChained<int>(\"refFld.someStruct.intFld\");\r\n    IPtr ref = ptr.GetRefChained(\"refFld.innerRef\");\r\n    \r\nTo read collections (example of supported: `Array`, `List<T>`, `Dictionary<TKey,TValue>`, `ConcurrentDictionary<TKey, TValue>`) I created the specialized wrappers. API usage example:\r\n\r\n    List<int> listOfInts = ptr.AsCollection().List()/*Wrapper is returned. I can get elements, size, etc.*/.Read<int>();\r\n    List<KeyValuePair<int, IPtr>> dictionary = ptr.AsCollection().Dictionary().Read<int, IPtr>(); //For Dictionary<int, object>\r\n\r\n\r\nTo read complex values:\r\n\r\n    DateTime dateTime = ptr.GetThisComplexValue().DateTime();\r\n    Guid guid = ptr.GetThisComplexValue().GuidValue();\r\n\r\n@JeffCyr comments:\r\nI like the idea of making extensible convertions. However, I prefer an approach where you don't have to specify the type of the field you are accessing or whether it's interior or not. It can be resolved quickly by inspecting the ClrInstanceField without much performance hit.\r\n\r\ne.g.\r\n```c#\r\nClrObject o = ...;\r\nint value = o[\"fieldName\"][\"innerField\"][\"intField\"].AsInt32();\r\n```\r\n\r\n```c#\r\nClrObject array = ...;\r\n\r\nforeach(ClrObject o in array)\r\n{\r\n    Console.WriteLine(o.Dynamic.fieldName.innerField.intField);\r\n}\r\n```\r\n\r\n@Zvirja comments:\r\n1. __Interior flag__ Great catch! Yes, you are right. We can understand whether it's interior or not by inspecting the current object type (structure or reference type). Likely, I missed that point some time ago. We should check how it works for the boxed structures thought...\r\n\r\n1. __Getting of the single field__ This is where I prefer to have extension method for each simple type. I like the `o.GetInt(\"intField\")` syntax much more than `(int)o[\"intFIeld'])` or `o[\"intField\"].AsInt32()`. That will allow to better follow the extension idea and to better handle the ambiguous situations (e.g. how to return string - as object wrapper, or by value). Also will allow us to get rid of redundant boxes (when you return `int` as `object` to unbox it later.) For large heaps that could be crucial.\r\n\r\n1. __Getting of the chain__ This is the place whether we could get benefit from rather dynamic model or the indexers suggested above: o[\"fieldName\"][\"innerField\"][\"intField\"]. They don't differ too much by idea, but we have to make API consistent and clearly document what `o[\"fld\"]` returns in any case. I hate current `var obj = field.GetValue(address)` model when you don't know what to expect as output.\r\n\r\n1. __Enumeration of the array__ For this case we can implement enumerator. However, iarray could contain `refs`, `structs` and `primitive`. I don't like idea of implicit casts, when you write `foreach(ClrObject o in array)` in one case and `foreach(int i in array)`. At least it will lead to boxes/unboxes. However, if you have nice idea how to implement this - let's discuss. E.g. we could mitigate this with getter for generic enumerator..\r\n\r\n1. __Enumeration of the advanced containers.__ I'd use the `IEnumerable` implementation for the arrays only and do not introduce that for the advanced collections like lists and dictionaries. That will make API very heavy and you will stop to understand what is returned. I like my way with extensions better for this situations `var list = ob.AsCollection().List(); var size = list.Count; var elems = list.Read<int>()`."},"comment":{"url":"https://api.github.com/repos/Microsoft/clrmd/issues/comments/146425403","html_url":"https://github.com/Microsoft/clrmd/issues/24#issuecomment-146425403","issue_url":"https://api.github.com/repos/Microsoft/clrmd/issues/24","id":146425403,"user":{"login":"JeffCyr","id":2624074,"avatar_url":"https://avatars.githubusercontent.com/u/2624074?v=3","gravatar_id":"","url":"https://api.github.com/users/JeffCyr","html_url":"https://github.com/JeffCyr","followers_url":"https://api.github.com/users/JeffCyr/followers","following_url":"https://api.github.com/users/JeffCyr/following{/other_user}","gists_url":"https://api.github.com/users/JeffCyr/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffCyr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffCyr/subscriptions","organizations_url":"https://api.github.com/users/JeffCyr/orgs","repos_url":"https://api.github.com/users/JeffCyr/repos","events_url":"https://api.github.com/users/JeffCyr/events{/privacy}","received_events_url":"https://api.github.com/users/JeffCyr/received_events","type":"User","site_admin":false},"created_at":"2015-10-08T05:37:12Z","updated_at":"2015-10-08T05:37:12Z","body":"That is great,\r\n\r\nSome thoughts:\r\n\r\n#####  GetInt32Field vs GetField<T>\r\nThe specific methods would be more auto-complete friendly\r\n\r\n##### Arrays\r\nI agree with @tomasr, conversion to array could be harmful. I suggest creating `struct ClrArray` with the same field as ClrObject, conversion would be a simple field copy. The struct could be `IEnumerable<ClrObject>` and have an indexed property for direct element access.\r\n"}}