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

How do I get delegate type???

Discussion in 'Scripting' started by Stef_Morojna, Oct 24, 2017.

  1. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    So what i'm trying to figure out is how to get the type necessary to create a delegate.

    I have tried to google everything, and can't find a answer that would work.
    Code (CSharp):
    1.     public delegate void UpdateFloatValueDelegate( float newValue);
    2.  
    3.     void Start(){
    4.  
    5.         // Not relevant to the problem
    6.         //for (int i = 0; i < values.Length; i++)
    7.             //for (int methodIndex = 0; methodIndex < values[i].methodsHolder.Length; methodIndex++){
    8.  
    9.                 // Gets method by name
    10.                 MethodInfo method = values[i].methodsHolder[methodIndex].component.GetType().GetMethod( values[i].methodsHolder[methodIndex].methodsName);
    11.  
    12.                 // Creates delegate
    13.                 UpdateFloatValueDelegate newDelegate = UpdateFloatValueDelegate.CreateDelegate( /* How do I do this? */ , method );
     
  2. hasanbayat

    hasanbayat

    Joined:
    Oct 18, 2016
    Posts:
    626
    The example at MSDN describes it as well: Delegate.CreateDelegate
    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using System.Security.Permissions;
    4.  
    5. // Declare three delegate types for demonstrating the combinations
    6. // of static versus instance methods and open versus closed
    7. // delegates.
    8. //
    9. public delegate void D1(C c, string s);
    10. public delegate void D2(string s);
    11. public delegate void D3();
    12.  
    13. // A sample class with an instance method and a static method.
    14. //
    15. public class C
    16. {
    17.     private int id;
    18.     public C(int id) { this.id = id; }
    19.  
    20.     public void M1(string s)
    21.     {
    22.         Console.WriteLine("Instance method M1 on C:  id = {0}, s = {1}",
    23.             this.id, s);
    24.     }
    25.  
    26.     public static void M2(string s)
    27.     {
    28.         Console.WriteLine("Static method M2 on C:  s = {0}", s);
    29.     }
    30. }
    31.  
    32. public class Example
    33. {
    34.     public static void Main()
    35.     {
    36.         C c1 = new C(42);
    37.  
    38.         // Get a MethodInfo for each method.
    39.         //
    40.         MethodInfo mi1 = typeof(C).GetMethod("M1",
    41.             BindingFlags.Public | BindingFlags.Instance);
    42.         MethodInfo mi2 = typeof(C).GetMethod("M2",
    43.             BindingFlags.Public | BindingFlags.Static);
    44.  
    45.         D1 d1;
    46.         D2 d2;
    47.         D3 d3;
    48.  
    49.  
    50.         Console.WriteLine("\nAn instance method closed over C.");
    51.         // In this case, the delegate and the
    52.         // method must have the same list of argument types; use
    53.         // delegate type D2 with instance method M1.
    54.         //
    55.         Delegate test =
    56.             Delegate.CreateDelegate(typeof(D2), c1, mi1, false);
    57.  
    58.         // Because false was specified for throwOnBindFailure
    59.         // in the call to CreateDelegate, the variable 'test'
    60.         // contains null if the method fails to bind (for
    61.         // example, if mi1 happened to represent a method of
    62.         // some class other than C).
    63.         //
    64.         if (test != null)
    65.         {
    66.             d2 = (D2) test;
    67.  
    68.             // The same instance of C is used every time the
    69.             // delegate is invoked.
    70.             d2("Hello, World!");
    71.             d2("Hi, Mom!");
    72.         }
    73.  
    74.  
    75.         Console.WriteLine("\nAn open instance method.");
    76.         // In this case, the delegate has one more
    77.         // argument than the instance method; this argument comes
    78.         // at the beginning, and represents the hidden instance
    79.         // argument of the instance method. Use delegate type D1
    80.         // with instance method M1.
    81.         //
    82.         d1 = (D1) Delegate.CreateDelegate(typeof(D1), null, mi1);
    83.  
    84.         // An instance of C must be passed in each time the
    85.         // delegate is invoked.
    86.         //
    87.         d1(c1, "Hello, World!");
    88.         d1(new C(5280), "Hi, Mom!");
    89.  
    90.  
    91.         Console.WriteLine("\nAn open static method.");
    92.         // In this case, the delegate and the method must
    93.         // have the same list of argument types; use delegate type
    94.         // D2 with static method M2.
    95.         //
    96.         d2 = (D2) Delegate.CreateDelegate(typeof(D2), null, mi2);
    97.  
    98.         // No instances of C are involved, because this is a static
    99.         // method.
    100.         //
    101.         d2("Hello, World!");
    102.         d2("Hi, Mom!");
    103.  
    104.  
    105.         Console.WriteLine("\nA static method closed over the first argument (String).");
    106.         // The delegate must omit the first argument of the method.
    107.         // A string is passed as the firstArgument parameter, and
    108.         // the delegate is bound to this string. Use delegate type
    109.         // D3 with static method M2.
    110.         //
    111.         d3 = (D3) Delegate.CreateDelegate(typeof(D3),
    112.             "Hello, World!", mi2);
    113.  
    114.         // Each time the delegate is invoked, the same string is
    115.         // used.
    116.         d3();
    117.     }
    118. }
    119.  
    120. /* This code example produces the following output:
    121.  
    122. An instance method closed over C.
    123. Instance method M1 on C:  id = 42, s = Hello, World!
    124. Instance method M1 on C:  id = 42, s = Hi, Mom!
    125.  
    126. An open instance method.
    127. Instance method M1 on C:  id = 42, s = Hello, World!
    128. Instance method M1 on C:  id = 5280, s = Hi, Mom!
    129.  
    130. An open static method.
    131. Static method M2 on C:  s = Hello, World!
    132. Static method M2 on C:  s = Hi, Mom!
    133.  
    134. A static method closed over the first argument (String).
    135. Static method M2 on C:  s = Hello, World!
    136. */
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Delegate declarations are just type declarations like any other. Just like a struct, enum, class, or anything. So you can use 'typeof' for it just like you would for any of the other types.

    Code (csharp):
    1. UpdateFloatValueDelegate newDelegate = System.Delegate.CreateDelegate( typeof(UpdateFloatValueDelegate), method );
     
  4. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    I have already tried that and well... it didn't work.

    upload_2017-10-24_16-33-13.png
    upload_2017-10-24_16-32-57.png

    Edit: Apparent I just had to cast it as UpdateFloatValueDelegate, I still don't know if this will actually work at the end (still work to do before I can run the whole thing), but lets hope for the best.

    Code (CSharp):
    1. UpdateFloatValueDelegate newDelegate = System.Delegate.CreateDelegate( typeof(UpdateFloatValueDelegate), method) as UpdateFloatValueDelegate;

    Edit 2: Yeah I should probably remember to google errors.
     

    Attached Files:

  5. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Don't overcomplicate it.

    Use the explicit constructor just like you'd do with other types:
    Code (csharp):
    1. UpdateFloatValueDelegate del = new UpdateFloatValueDelegate(TARGET_METHOD);
    or use simplified syntax (compiler takes care of the rest):
    Code (csharp):
    1. UpdateFloatValueDelegate del = TARGET_METHOD;
    Both of them are better then your approach, as yours is less efficient and less type-safe.
     
  6. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    Dosen't work, my method reference is a "MethodInfo".

    And I can't just do this, because I want to be able to target any method with my parameters in any script in my project.
    Code (CSharp):
    1. UpdateFloatValueDelegate del = TARGET_METHOD
     
    Last edited: Oct 24, 2017
  7. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    So right now I am here.
    The code does not give me any errors.

    Code (CSharp):
    1. public delegate void UpdateFloatValueDelegate( float newValue);
    2.  
    3.                 // Gets method by name
    4.                 // This method is always a return type void, and has 1 float parameter, just like my delegate
    5.                 MethodInfo method = values[i].methodsHolder[methodIndex].component.GetType().GetMethod(values[i].methodsHolder[methodIndex].methodsName);
    6.  
    7.                 // Creates delegate
    8.                 UpdateFloatValueDelegate newDelegate = UpdateFloatValueDelegate.CreateDelegate( typeof(UpdateFloatValueDelegate), method) as UpdateFloatValueDelegate;
    However when I run it, this error appears. ( Btw my method and my delegate are both return type void, and have 1 float parameter)
    Code (CSharp):
    1. ArgumentException: method argument length mismatch
    2. System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:226)
    3. System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:291)
    4. System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:295)
    5. FloatValue.Start () (at Assets/Scripts/Module/FloatValue.cs:22)
     
    Last edited: Oct 24, 2017
  8. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I see.

    Have you considered other approaches?
    This can really be error-prone, just as you experience it right now.

    I'd rather register such components using an interface instead, because that'd be much more type-safe.

    However, I assume you want to hook up components via inspector and specify a name given as string to be "flexible". But this is really error-prone and involves alot overhead for reflection and all validations/checks that are strongly required for correct behaviour.

    The error is not within your snippets.
    Some "component" in that array seems to be of a type which declares, inherits [...] a method with the given name which does not match the signature.
    Not sure if this could possibly be an overload.

    What you have to do before creating the delegate is more validation, i.e. if you actually received a method that matches the signature. You should generelly not assume you'll always get a method with that particular signature from that container, unless you encapsulated it properly and already ensure exactly that during registration to that container.

    This is more of a 'remember this' - agreement rather than a type-based (and type-safe) contract you have to fullfill (interfaces).
     
  9. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    So what a tried was:
    Code (CSharp):
    1. Component component = values[i].methodsHolder[methodIndex].component;
    2.                 string methodName = values[i].methodsHolder[methodIndex].methodsName;
    3.  
    4.                 // Creates delegate
    5.                 UpdateFloatValueDelegate newDelegate = UpdateFloatValueDelegate.CreateDelegate( typeof(UpdateFloatValueDelegate), component, methodName) as UpdateFloatValueDelegate;
    And everything works 100% fine.

    Btw I also checked if I was getting the correct method by printing the method name, and I was getting the correct one, no idea what the error is then.

    So about other approaches, I really cant, and also my way of getting the method name is pretty solid and error proof. There is pretty much no way I can select a method that dosent exist or is not valid, unless I deleted it in script or change its parameters or something.

    Here is the custom editor that lets me find the method.
    upload_2017-10-24_19-56-58.png

    upload_2017-10-24_19-57-58.png

    So a dropdown menu with all the functions of the target component that match the parameters of the delegate.

    Also here is the editor code if anyone is interested.
    Code (CSharp):
    1.     public override void OnInspectorGUI(){
    2.  
    3.         FloatValue floatValue = (FloatValue)target;
    4.  
    5.         // Loops once for each value
    6.         for (int i = 0; i < floatValue.values.Length; i++){
    7.  
    8.             GUILayout.BeginHorizontal();
    9.             GUILayout.Space(50);
    10.  
    11.             floatValue.values[i].floatValue = EditorGUILayout.FloatField( floatValue.values[i].floatValue);
    12.             floatValue.values[i].label = EditorGUILayout.ColorField( GUIContent.none, floatValue.values[i].label, false, false, false, new ColorPickerHDRConfig(0, 0, 0, 0));
    13.  
    14.             //if (i < floatValue.updateFloatValueDelegates.Length - 2)
    15.             //if (floatValue.updateFloatValueDelegates[i] != null)
    16.             //EditorGUILayout.SelectableLabel( floatValue.updateFloatValueDelegates[i].Method.ToString());
    17.  
    18.             #region Method selecting
    19.  
    20.             // Loops once for each selet method menu
    21.             for (int methodIndex = 0; methodIndex < floatValue.values[i].methodsHolder.Length; methodIndex++){
    22.  
    23.                 EditorGUILayout.EndHorizontal();
    24.                 EditorGUILayout.BeginHorizontal();
    25.  
    26.                 GUILayout.Label( "Slot", GUILayout.Width(35));
    27.  
    28.                 floatValue.values[i].methodsHolder[methodIndex].component = (Component)EditorGUILayout.ObjectField(floatValue.values[i].methodsHolder[methodIndex].component, typeof(Component));
    29.  
    30.                 if (floatValue.values[i].methodsHolder[methodIndex].component){
    31.  
    32.                     List<string> methodNames = FindMethodsNames(floatValue.values[i].methodsHolder[methodIndex].component);
    33.  
    34.                     if (methodNames.Count > 0){
    35.  
    36.                         methodNames.Add(floatValue.values[i].methodsHolder[methodIndex].methodsName);
    37.  
    38.                         int selected = EditorGUILayout.Popup(methodNames.Count - 1, methodNames.ToArray());
    39.  
    40.                         floatValue.values[i].methodsHolder[methodIndex].methodsName = methodNames[selected];
    41.  
    42.                     } else {
    43.  
    44.                         Color oldColor = GUI.color;
    45.                         GUI.color = Color.red;
    46.                         GUILayout.Label("No methods to select");
    47.                         GUI.color = oldColor;
    48.                     }
    49.                 }
    50.  
    51.                 EditorGUILayout.EndHorizontal();
    52.                 EditorGUILayout.BeginHorizontal();
    53.             }
    54.      
    55.         #endregion
    56.  
    57.             GUILayout.Space(5);
    58.             GUILayout.EndHorizontal();
    59.         }
    60.  
    61.         #region Button
    62.         GUILayout.BeginHorizontal();
    63.         GUILayout.Space(50);
    64.  
    65.         if (GUILayout.Button("Add New")){
    66.  
    67.             List<FloatValue.Holder> valuesList = new List<FloatValue.Holder>(floatValue.values);
    68.             valuesList.Add(new FloatValue.Holder());
    69.             floatValue.values = valuesList.ToArray();
    70.         }
    71.  
    72.         if (GUILayout.Button("Delete Last")){
    73.  
    74.             List<FloatValue.Holder> valuesList = new List<FloatValue.Holder>(floatValue.values);
    75.             valuesList.RemoveAt(valuesList.Count - 1);
    76.             floatValue.values = valuesList.ToArray();
    77.         }
    78.         GUILayout.Space(5);
    79.         GUILayout.EndHorizontal();
    80.         #endregion
    81.  
    82.         base.OnInspectorGUI();
    83.     }
    84.  
    85.     List<string> FindMethodsNames(object pObject){
    86.  
    87.         MethodInfo[] pMethodInfoList = pObject.GetType().GetMethods();
    88.  
    89.         List<string> matchingMethods = new List<string>();
    90.  
    91.         foreach (MethodInfo pMethodInfo in pMethodInfoList)
    92.             if (pMethodInfo.ReturnType.Name == "Void" && pMethodInfo.GetParameters().Length == 1 && pMethodInfo.GetParameters()[0].ParameterType.Name == "Single")
    93.                 matchingMethods.Add(pMethodInfo.Name);
    94.  
    95.         return matchingMethods;
    96.     }
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This looks an awful lot like Unity's built-in event system. Any particular reason you're not just using that instead?