Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Can't access private setter using Reflection

Discussion in 'Experimental Scripting Previews' started by StephanieRowlinson, Jun 21, 2017.

  1. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    I've upgraded our project to 2017.1 and everything seems to be working fine except that I know get this error when I try to place an object from our library.

    ArgumentException: Set Method not found for 'AutoDisembark'
    System.Reflection.MonoProperty.SetValue (System.Object obj, System.Object value, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] index, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoProperty.cs:433)
    XmlHelper.SetObjectValue (System.Reflection.PropertyInfo _cPropInfo, System.Object _cObject, System.Object _cValue, System.Object[] _cIndex) (at Assets/Logic/PlatformPending/Serialization/XmlHelper.cs:1875)
    XmlHelper.DeserializeCreateProperty (System.Object _cObject, XmlHelper+AllPropertyInfo _cPropInfo, System.Xml.XmlElement _cElement, System.Type _tAttributeType, ConfigVersion _cStartingVersion, ConfigVersion _cStartingProductBase) (at Assets/Logic/PlatformPending/Serialization/XmlHelper.cs:687)
    XmlHelper.DeserializeCreate (IHasSaveProperties _cObject, System.Xml.XmlElement _cElement, System.Type _tAttributeType, ConfigVersion _cStartingVersion, ConfigVersion _cStartingProductBase) (at Assets/Logic/PlatformPending/Serialization/XmlHelper.cs:646)
    XVR.RM.Entities.Location.DeserializeCreate (System.Xml.XmlElement _cElement, System.Type _tAttributeType, ConfigVersion _cStartingVersion, ConfigVersion _cStartingProductBase) (at Assets/Logic/Product/DataEntities/Locations/Location.cs:827)
    XVR.RM.Modules.TemplateModule+<DeserializeLocation>c__AnonStorey5.<>m__0 (XVR.RM.Entities.Location _cLocation) (at Assets/Logic/Product/Modules/TemplateModule/TemplateModule.cs:1018)
    XVR.RM.Modules.LocationModule.AssembleLocation (XVREventInfo _cEventInfo, System.Type _cType, XVR.RM.Modules.LocationModule+PreAssembledDelegate _cDelegate, System.String _sName, System.String _sImagePath, System.String _sIconPath, XVR.Spatial.MapPoint _cMapPoint, System.Guid _cGUID, System.Boolean _bIsGenerated) (at Assets/Logic/Product/Modules/LocationModule/LocationModule.cs:377)
    XVR.RM.Modules.TemplateModule.DeserializeLocation (XVREventInfo _cEventInfo, System.Xml.XmlElement _cLocationElement, System.Type _tAttributeType) (at Assets/Logic/Product/Modules/TemplateModule/TemplateModule.cs:1014)
    XVR.RM.Modules.TemplateModule.CreateLocationTemplate (XVREventInfo _cEventInfo, XVR.RM.LocationTemplateXmlRoot _cTemplate, System.Boolean _bIsGenerated) (at Assets/Logic/Product/Modules/TemplateModule/TemplateModule.cs:972)
    XVR.RM.Modules.TemplateModule.AssembleLocationTemplate (XVREventInfo _cEventInfo, XVR.RM.LocationTemplateXmlRoot _cTemplate, System.Boolean _bIsGenerated) (at Assets/Logic/Product/Modules/TemplateModule/TemplateModule.cs:955)
    XVR.RM.Modules.PlacementModule.PlaceLocationTemplate (XVREventInfo _cEventInfo, XVR.RM.LocationTemplateXmlRoot _cTemplate, UnityEngine.Vector2 _vPositionToPlace, System.Boolean _bIsGenerated) (at Assets/Logic/Product/Modules/PlacementModule/PlacementModule.cs:310)
    XVR.RM.GUI.LocationLibraryGUI+<UpdateGUI>c__AnonStorey0.<>m__1 (XVR.GUI.Controls.DragDropButton btn, UnityEngine.Vector2 pos) (at Assets/Logic/Product/Modules/LocationModule/GUI/LocationLibraryGUI.cs:198)
    XVR.GUI.Controls.DragDropButton.DragDropButton_OnMouseDragEnd (XVR.GUI.GUIComponent _cComponent, UnityEngine.EventSystems.PointerEventData _cEventData) (at Assets/Logic/Platform/GUI/GUICore/Controls/DragDropButton.cs:112)
    XVR.GUI.GUIComponent.OnMouseDragEndCallback (UnityEngine.EventSystems.PointerEventData _cEventData) (at Assets/Logic/Platform/GUI/GUICore/GUIComponent.cs:605)
    XVR.GUI.GUIOnMouseDragEnd.OnEndDrag (UnityEngine.EventSystems.PointerEventData eventData) (at Assets/Logic/Platform/GUI/GUICore/Events/GUIOnMouseDragEnd.cs:16)
    UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IEndDragHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:78)
    UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:261)
    UnityEngine.EventSystems.EventSystem:Update()

    It appears that SetValue can no longer find the private setter. I've tried different BindingFlags and different ways of getting the method (see below), but none of them seem to work.

    The property I'm trying to access:
    Code (CSharp):
    1.  
    2. public bool AutoDisembark
    3. {
    4.     get { return m_bAutoDisembark; }
    5.     private set
    6.     {
    7.          AutoDisembarkSet(value);
    8.     }
    9. }
    10.  
    Attempts to access it:
    Code (CSharp):
    1.  
    2. var setterMethod = _cPropInfo.GetSetMethod(true);
    3. if (setterMethod != null)
    4.      setterMethod.Invoke(_cObject, new[] { value });
    5.  
    6. _cPropInfo.SetValue(_cObject, value, BindingFlags.NonPublic | BindingFlags.Instance| BindingFlags.InvokeMethod | BindingFlags.SetProperty, null, _cIndex, null);
    7.  
    Am I missing something or have I found a bug?
     
  2. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,931
    Is this with the new Mono runtime?
     
  3. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    Ah yes, sorry forgot to add this only happens when I switch to .NET 4.6.
     
  4. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,931
    Ok, thanks. This does look like a bug. Can you submit a bug report with a project that causes this issue?
     
  5. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    Okay, I'll make a new test project and submit a bug report.
     
  6. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    Turns out to be due to the private setter being in an inherited class. I've submitted a bug report, but perhaps this is an intentional change in the behaviour of .NET? I've had a hard time finding documentation on the changes in the .NET framework from the version we were on to 4.6.
     
  7. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Could you show an example? I was curious to reproduce the bug but failed to reproduce the "private setter being in an inherited class".
     
  8. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Something like this?

    Code (CSharp):
    1. public class Derived : Base { }
    2.  
    3. public class Base
    4. {
    5.        public bool AutoDisembark { get; private set; }
    6. }
    Code (CSharp):
    1. var instance = new Derived();
    2. var property = typeof(Derived).GetProperty("AutoDisembark", BindingFlags.Public | BindingFlags.Instance);
    3.  
    4. // method 1
    5. property.SetValue(instance, true);
    6.  
    7. // method 2
    8. var setter = property.GetSetMethod(true);
    9. setter.Invoke(instance, new object[] { true });
     
    Last edited: Jun 21, 2017
  9. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    I suspect that perhaps BindingFlags.FlattenHierarchy is necessary.
     
  10. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    This code fails for me, but ONLY when I'm using the .NET 4.6 framework. Which is why I thought it might be a bug, but apparently .NET has just changed the rules for accessing inherited properties since 2.0

    Code (CSharp):
    1.  
    2.  
    3. public class Reflection : MonoBehaviour
    4. {
    5.     private ClassWithProperties m_cClassWithProperties;
    6.  
    7.     // Use this for initialization
    8.     void Start()
    9.     {
    10.         m_cClassWithProperties = new ClassWithProperties();
    11.     }
    12.  
    13.     // Update is called once per frame
    14.     void Update()
    15.     {
    16.         Debug.Log("Before " + m_cClassWithProperties.APropertyWithPrivateSetter);
    17.         PropertyInfo info = typeof(ClassWithProperties).GetProperty(nameof(ClassWithProperties.APropertyWithPrivateSetter));
    18.         info.SetValue(m_cClassWithProperties, true);
    19.  
    20.         Debug.Log("After " + m_cClassWithProperties.APropertyWithPrivateSetter);
    21.     }
    22. }
    23.  
    24.  
    25. public class BaseClassWithProperties
    26. {
    27.     private bool m_bAProperty;
    28.  
    29.     public bool APropertyWithPrivateSetter
    30.     {
    31.         get { return m_bAProperty; }
    32.         private set
    33.         {
    34.             m_bAProperty = value;
    35.         }
    36.     }
    37. }
    38.  
    39. public class ClassWithProperties : BaseClassWithProperties
    40. {
    41.  
    42. }
    43.  
     
  11. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    From MSDN:
    I don't think this is the BindingFlag I'm looking for.
     
  12. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    This one works in Net 4.6:
    Code (CSharp):
    1. var property = typeof(Derived).GetProperty("AutoDisembark", BindingFlags.Public | BindingFlags.Instance);
    2. property = property.DeclaringType().GetProperty(property.Name);
     
  13. StephanieRowlinson

    StephanieRowlinson

    Joined:
    Jul 23, 2014
    Posts:
    137
    Thanks that does indeed work for this particular property.
     
  14. joncham

    joncham

    Unity Technologies

    Joined:
    Dec 1, 2011
    Posts:
    276
    Yes, I am pretty sure this is a case where the behavior changed with new .NET or Mono fixing a bug to match .NET behavior.