Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[SOLVED] Can I re-write this more efficiently?

Discussion in 'Scripting' started by Deleted User, May 15, 2015.

  1. Deleted User

    Deleted User

    Guest

    I'm using Reflection to get the values of all the properties in an event. I plan on using this on mobiles and from my tests, GetValue is quite the performance user. Is there a way to write this I may have missed that is more efficient?

    Code (CSharp):
    1.  
    2. public class AutoBehaviour : ComponentBehaviour
    3.         {
    4.             public Event Event { get; set; }
    5.             public ICondition ObjectCondition { get; set; }
    6.             public List<DynamicAction> Actions { get; set; }
    7.  
    8.             private PropertyInfo[] _eventProperties;
    9.             private object[] _eventValues;
    10.  
    11.             public void Run<T>(T e) where T : Event
    12.             {
    13.                 for (int i = 0; i < _eventProperties.Length; i++)
    14.                 {
    15.                     var value = _eventProperties[i].GetValue(e,null);
    16.                     if (_eventValues[i] == null || _eventValues[i] == default(object)) continue;
    17.                     if (value != null && _eventValues[i] != value) return;
    18.                 }
    19.                 Evaluate();
    20.             }
    21.  
    22.             public void Evaluate()
    23.             {
    24.                 if (ObjectCondition.IsMetBy(shell)) Execute();
    25.             }
    26.  
    27.             public void Execute()
    28.             {
    29.                 foreach (var action in Actions) action.Execute(new ExecutionArgs<Shell>(shell, shell));
    30.             }
    31.  
    32.             protected override void Awake()
    33.             {
    34.                 base.Awake();
    35.                 var type = Event.GetType();
    36.                 _eventProperties = type.GetProperties();
    37.                 shell.Messenger.AddListener(type, Run);
    38.                 for (int i = 0; i < _eventProperties.Length; i++) _eventValues[i] = _eventProperties[i].GetValue(Event, null);
    39.             }
    40.         }
    41.  
     
    Last edited by a moderator: May 15, 2015
  2. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    Deleted User likes this.
  3. Deleted User

    Deleted User

    Guest

    Thanks but Emit doesn't work on iOS. :(
     
  4. Deleted User

    Deleted User

    Guest

  5. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    Not sure about this but you can try wrapping the values into a class. That way you can use only 1 GetValue on the reference (if even needed).
    And are you sure there is not a type safe way to do what you need?
     
    Last edited: May 15, 2015
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    What is it you're attempting to do in 'Run'?

    You loop over EVERY property on the object and check if it's null or default values, and if so block evaluation?

    If I knew what you were attempting to accomplish, I may be able to think of a better approach.


    Also, there is no need for generics on 'Run'. The T is used for nothing but constraining the input parameter to the type Event. Typing the input to 'Event' would do the same thing.

    Code (csharp):
    1.  
    2. public void Run(Event e)
    3. {
    4. ...
    5.  
     
  7. Deleted User

    Deleted User

    Guest

    yeah @lordofduct's help. :)

    Thanks for pointing the generic thing out: I forgot to remove it.

    Basically, I want to register the method 'Run' to an event via the inspector (through 'public Event Event{ get; set; }'). It was easy enough to do using the method 'shell.Messenger.AddListener(type, Run);' to register it to my messenger system. Then--here's the part I'm having troubled with--I wanted that when the event the method 'Ran' is subscribed to fires, the 'Run' method checks the value of the parameters of the event that fired vs. the value of the parameters of the event assigned in the script. If the parameters are equal, the method 'Execute' is ran.
     
    Last edited by a moderator: May 18, 2015
  8. Deleted User

    Deleted User

    Guest

    Found a way to also fix one mistake in the script I posted: the '=='operator needs to be replaced with .Equals() to test for equality if the value retrieved are value-types.
     
  9. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    To me, the only reason why you have to use reflection is because of the messaging system. You could try restricting the event type to Action <object> where the parameter is the message wrapped in a class. Then you could just do a type check, cast, and continue on.
     
  10. Deleted User

    Deleted User

    Guest

    What you're suggesting sounds interesting but I'm having a hard time visualizing how it would work. Could you show me a short example?
     
  11. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    Here is an example of what I was talking about earlier:
    Code (CSharp):
    1. Dictionary<Type, List<Action<object, object>>> _listenersSortedByMessageType = new Dictionary<Type, List<Action<object, object>>>();
    2.  
    3. void Subscribe(Type msgType, Action<object, object> listener)
    4. {
    5. //get or add key (msgType)
    6. //add listener to list
    7. }
    8.  
    9. void SendMessage(Type msgType, object sender, object msg)
    10. {
    11. //msgType is optional for hierarchies, etc.
    12. //Get key, invoke all in list
    13. }
    14.  
    15. //Handler
    16. void SubToMessenger()
    17. {
    18. messenger.Subscribe(typeof(MessageType), TestHandler);
    19. }
    20.  
    21. //can add attributes for type checking, etc
    22. //however, the programmer KNOWS what the message type
    23. //should be and a type check can be avoided for a straight
    24. //cast if desired
    25. void TestHandler(object sender, object message)
    26. {
    27. var convertedMSG = (MessageType)message;
    28. }
    There is an actual type safe way to do this using generics to be inserted as in Action<object, MessageType>. However, some preliminary benchmarking suggests that this method would be slower because of the casting required during storage and retrieval of the delegate list (or I'm being dumb).

    EDIT:
    Example with what I believe is your current system, I just like the above better because it avoids invoking unnecessary delegates.
    Code (CSharp):
    1. void Handler(object message)
    2. {
    3. if(message.GetType()!=expectedType)
    4. return;
    5. //Cast message
    6. // etc...
    7. }
     
    Last edited: May 19, 2015
    Deleted User likes this.
  12. Deleted User

    Deleted User

    Guest

    OK Maybe I kinda understand where you're going with this. But in your example, how do you cast the message? What do you cast it as? Why are you casting it? I don't know of any way to cast dynamically, so how would I compare the properties of the message raised vs. the properties of the event subscribed to for the method to run.

    I.E. If you had a "ItemPickedUpEventArgs", In it, you might have information about what item was picked-up. The system above would allow you to specify what item specifically you want to be picked-up for it to fire. The event would be raised regardless of what item was picked up, but the script above would only "Execute" if the item picked up matches the item specified in the "Event" property.
     
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    Could you post a use case for this? As in some code where you're using this event system.

    I'm suspecting that you could simplify it a lot with some built-in event system - either Actions and Functions. The conditions should probably be a typed Predicate, in which case you should be able to ditch all reflection.
     
  14. Deleted User

    Deleted User

    Guest

    Sure, the code in the OP is where I use it. But because I attach it to GameObjects I want it to be modifiable in the Inspector. Event, ICondition, and DynamicAction list are all serializable, which is why I don't use Predicate / Function.
    Basically, I just attach the code I posted to a GameObject, and then edit the properties in the Inspector. I can post a screenshot if you'd like?

    Solved! Random_Civilian 's solution was quite good.
     
    Last edited by a moderator: May 20, 2015