简体   繁体   中英

Reflection C# - How to get handle to class several properties deep

I am not new to OOP, but am new to Reflection. I'm sure I'm missing something simple and have experimented for some time before posting.

I have been trying to get access to a field, both get and set, its value. The issue is the field is within property of a class within a property of a class.

The following code gets an existing window and works deeper into the classes until I hit a wall. Ultimately I want to get and set "k_LineHeight" inside of an existing instance of TreeViewGUI.

The following code is heavily annotated. Thank you for taking the time to look at this. In Unity's Mono Debug.Log() is the equivalent of write to the console.

// Get the assembly
Assembly asm = typeof(UnityEditor.EditorWindow).Assembly;
Debug.Log (asm + "\n"); // returns -> UnityEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

// Get the Type of SceneHierarchyWindow from 'asm'
Type wndType = asm.GetType("UnityEditor.SceneHierarchyWindow");
Debug.Log (wndType + "\n"); // returns -> UnityEditor.SceneHierarchyWindow <- Type

// 'GetWindow' retrieves the ACTIVE instance of the windows currently open
EditorWindow wnd = EditorWindow.GetWindow(wndType);
Debug.Log (wnd + "\n"); // returns -> U (UnityEditor.SceneHierarchyWindow) <- Active Object

// Retrieves the ACTIVE TreeView class stored in 'treeView' from 'wnd'
var treeViewVal = wndType.GetProperty("treeView", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(wnd, null);
Debug.Log (treeViewVal + "\n"); // returns -> UnityEditor.TreeView

// Retrieves the property 'state' that is (public TreeViewState state { get; set; }) inside of 'treeViewVal'
var stateVal = treeViewVal.GetType().GetProperty("state").GetValue(treeViewVal, null);
Debug.Log (stateVal + "\n"); // returns UnityEditor.TreeViewState

// Retrive the value of the field 'scrollPos' (public Vector2 scrollPos;)  inside of the 'stateVal'
var v2 = (Vector2)stateVal.GetType ().GetField ("scrollPos").GetValue (stateVal);
Debug.Log (v2 + "\n"); // returns -> (0.0, 0.0)

/// ALL OF THE ABOVE WORKS AS EXPECTED THE FOLLOWING IS WHERE IM STUMPED ///

// Retrieves the property 'gui' that is ( internal class GameObjectTreeViewGUI : TreeViewGUI) inside of 'treeViewVal'
var guiVal = treeViewVal.GetType().GetProperty("gui").GetValue(treeViewVal, null);
Debug.Log (guiVal + "\n"); // returns -> UnityEditor.GameObjectTreeViewGUI <- Type but I believe I need the Object

// THIS is where I'm stuck...

// Should retrieve the value of the field 'k_LineHeight' (protected float k_LineHeight = 16f;) inside of 'TreeViewGUI' the 
var k_Line = guiVal.GetType().GetField ("k_LineHeight").GetValue (guiVal);
Debug.Log (k_Line + "\n"); // returns -> NullReferenceException: Object reference not set to an instance of an object       

// If I attempt to ...
Type testType = guiVal.GetType ();
object test = Activator.CreateInstance(testType);
Debug.Log (test + "\n"); // returns -> Method not found: 'Default constructor not found...ctor() of UnityEditor.GameObjectTreeViewGUI'. 

UPDATE :

// ORIGINAL LINE
        var k_LineA = guiVal.GetType().GetField ("k_LineHeight").GetValue(guiVal); 
        Debug.Log (k_LineA + "\n"); // returns-> Object reference not set to an instance of an object.

    // SUGGESTED PART ONE
        var k_LineB = guiVal.GetType().BaseType;
        Debug.Log (k_LineB + "\n"); // returns -> UnityEditor.TreeViewGUI <- This is correct

    // SUGGESTED PART TWO - > Have tried with an without various flags
        var k_LineC = guiVal.GetType().BaseType.GetField("k_LineHeight", BindingFlags.Instance | 
            BindingFlags.Static |
            BindingFlags.NonPublic |
            BindingFlags.Public).GetValue(guiVal);
        Debug.Log (k_LineC + "\n"); // returns -> Object reference not set to an instance of an object.
    // GetValue wants guiVal to be an object???

    // First part of UnityEditor.TreeViewGUI Class
        namespace UnityEditor
        {
            internal abstract class TreeViewGUI : ITreeViewGUI
            {
                protected PingData m_Ping = new PingData();
                private bool m_AnimateScrollBarOnExpandCollapse = true;
                protected float k_LineHeight = 16f;
                protected float k_BaseIndent = 2f;
                protected float k_IndentWidth = 14f;
                protected float k_FoldoutWidth = 12f;
                protected float k_IconWidth = 16f;
                protected float k_SpaceBetweenIconAndText = 2f;
                protected float k_HalfDropBetweenHeight = 4f;
                protected TreeView m_TreeView;
                ...

Thanks to your suggestion I am now into the proper class. Feel silly I didn't think of that. Unfortunately its still returning the same during access. GetValue wants guiVal to be an object???

Seems closer, anyone have additional thoughts?

您正在尝试记录对象而不是类型。类型是对象的属性,定义对象是哪种类型,而对象是类

k_LineHeight isn't a field in UnityEditor.GameObjectTreeViewGUI .

https://github.com/MattRix/UnityDecompiled/blob/master/UnityEditor/UnityEditor/GameObjectTreeViewGUI.cs

GameObjectTreeViewGUI inherits from TreeViewGUI . I googled and didn't find the source for that. But assuming that it is a field in TreeViewGUI you could do

var k_Line = guiVal.GetType().BaseType.GetField ("k_LineHeight").GetValue (guiVal);
                               ^^^

ANSWER : The "Update" on my side had a typo. The answer to this is the one is a combination of both TheHenny's and Scott's. I was attempting to access a variable only visible in the BaseType. Thank you both!

Final code:

        var k_LineC = guiVal.GetType().BaseType.GetField("k_LineHeight", BindingFlags.Instance | 
        BindingFlags.Static |
        BindingFlags.NonPublic |
        BindingFlags.Public).GetValue(guiVal);
        Debug.Log (k_LineC + "\n"); // <-- returns 16

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM