Skip to content

Commit eb75649

Browse files
author
Fraser Greenroyd
authored
BHoM_Engine: RunExtensionMethodAsync and TryRunExtensionMethodAsync added (#3191)
2 parents cc447e1 + 181d3c2 commit eb75649

File tree

2 files changed

+121
-36
lines changed

2 files changed

+121
-36
lines changed

BHoM_Engine/Compute/RunExtensionMethod.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@
2020
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
2121
*/
2222

23+
using BH.oM.Base;
2324
using BH.oM.Base.Attributes;
24-
using System;
25-
using System.Collections.Concurrent;
2625
using System.ComponentModel;
27-
using System.Linq;
28-
using System.Reflection;
26+
using System.Threading.Tasks;
2927

3028
namespace BH.Engine.Base
3129
{
@@ -65,6 +63,39 @@ public static object RunExtensionMethod(object target, string methodName, object
6563
}
6664

6765
/***************************************************/
66+
67+
[Description("Asynchronously runs an extension method accepting a single argument based on a provided object and method name.\n" +
68+
"Finds the method via reflection the first time it is run, then compiles it to a function and stores it for subsequent calls.")]
69+
[Input("target", "The object to find and run the extension method for.")]
70+
[Input("methodName", "The name of the method to be run.")]
71+
[Output("result", "The result of the method execution. If no method was found, null is returned.")]
72+
public static async Task<object> RunExtensionMethodAsync(object target, string methodName)
73+
{
74+
Output<bool, object> result = await TryRunExtensionMethodAsync(target, methodName);
75+
if (result.Item1)
76+
return result.Item2;
77+
else
78+
return null;
79+
}
80+
81+
/***************************************************/
82+
83+
[Description("Asynchronously runs an extension method accepting multiple arguments based on a provided main object and method name and additional arguments.\n" +
84+
"Finds the method via reflection the first time it is run, then compiles it to a function and stores it for subsequent calls.")]
85+
[Input("target", "The first of the argument of the method to find and run the extention method for.")]
86+
[Input("methodName", "The name of the method to be run.")]
87+
[Input("parameters", "The additional arguments of the call to the method, skipping the first argument provided by 'target'.")]
88+
[Output("result", "The result of the method execution. If no method was found, null is returned.")]
89+
public static async Task<object> RunExtensionMethodAsync(object target, string methodName, object[] parameters)
90+
{
91+
Output<bool, object> result = await TryRunExtensionMethodAsync(target, methodName, parameters);
92+
if (result.Item1)
93+
return result.Item2;
94+
else
95+
return null;
96+
}
97+
98+
/***************************************************/
6899
}
69100
}
70101

BHoM_Engine/Compute/TryRunExtensionMethod.cs

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,15 @@
2020
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
2121
*/
2222

23-
using BH.oM.Base.Debugging;
23+
using BH.oM.Base;
24+
using BH.oM.Base.Attributes;
2425
using System;
26+
using System.Collections.Concurrent;
2527
using System.Collections.Generic;
26-
using System.Diagnostics;
27-
using System.IO;
28+
using System.ComponentModel;
2829
using System.Linq;
29-
using System.Runtime.CompilerServices;
3030
using System.Reflection;
31-
using System.Text;
3231
using System.Threading.Tasks;
33-
using System.ComponentModel;
34-
using BH.oM.Base.Attributes;
35-
using System.Collections.Concurrent;
3632

3733
namespace BH.Engine.Base
3834
{
@@ -69,22 +65,99 @@ public static bool TryRunExtensionMethod(this object obj, string methodName, obj
6965
return TryRunExtensionMethod(methodName, new object[] { obj }.Concat(parameters).ToArray(), out result);
7066
}
7167

68+
/***************************************************/
69+
70+
[Description("Looks for an extension method applicable to the input object with the provided `methodName` and, if found, invokes it asynchronously.\n" +
71+
"Extension methods are searched using Reflection through all BHoM assemblies.\n" +
72+
"If no method is found, this returns `false`, and the `result` is null.")]
73+
[Input("obj", "Object whose extension method is to be found, and to which the method will be applied in order to obtain the result.")]
74+
[Input("methodName", "Name of the extension method defined for the input object that is to be found in any of the BHoM assemblies.")]
75+
[Output("First output: true if a method was found and an invocation was attempted. False otherwise." +
76+
"\nSecond output: result of the call if an attempt was made.")]
77+
public static async Task<Output<bool, object>> TryRunExtensionMethodAsync(this object obj, string methodName)
78+
{
79+
return await TryRunExtensionMethodAsync(methodName, new object[] { obj });
80+
}
81+
82+
/***************************************************/
83+
84+
[Description("Looks for an extension method applicable to the input object with the provided `methodName` and and, if found, invokes it asynchronously.\n" +
85+
"Extension methods are searched using Reflection through all BHoM assemblies.\n" +
86+
"If no method is found, this returns `false`, and the `result` is null.")]
87+
[Input("obj", "Object whose extension method is to be found, and to which the method will be applied in order to obtain the result.")]
88+
[Input("methodName", "Name of the extension method defined for the input object that is to be found in any of the BHoM assemblies.")]
89+
[Input("parameters", "The additional arguments of the call to the method, skipping the first argument provided by 'target'.")]
90+
[Output("First output: true if a method was found and an invocation was attempted. False otherwise." +
91+
"\nSecond output: result of the call if an attempt was made.")]
92+
public static async Task<Output<bool, object>> TryRunExtensionMethodAsync(this object obj, string methodName, object[] parameters)
93+
{
94+
return await TryRunExtensionMethodAsync(methodName, new object[] { obj }.Concat(parameters).ToArray());
95+
}
96+
7297
/***************************************************/
7398
/**** Private Methods ****/
7499
/***************************************************/
75100

76101
[Description("Runs the requested method and returns the result. For performance reasons compiles the method to a function the first time it is run, then stores it for subsequent calls.")]
77102
private static bool TryRunExtensionMethod(string methodName, object[] parameters, out object result)
78103
{
79-
if (parameters == null || parameters.Length == 0 || parameters.Any(x => x == null) || string.IsNullOrWhiteSpace(methodName))
104+
Func<object[], object> func = ExtensionMethodToRun(methodName, parameters);
105+
106+
// Try calling the method
107+
try
108+
{
109+
if (func == null)
110+
{
111+
result = null;
112+
return false;
113+
}
114+
else
115+
{
116+
result = func(parameters);
117+
return true;
118+
}
119+
}
120+
catch (Exception e)
80121
{
122+
BH.Engine.Base.Compute.RecordError($"Failed to run {methodName} extension method.\nError: {e.Message}");
81123
result = null;
82124
return false;
83125
}
126+
}
127+
128+
/***************************************************/
129+
130+
[Description("Asynchronously runs the requested method and returns the result. For performance reasons compiles the method to a function the first time it is run, then stores it for subsequent calls.")]
131+
private static async Task<Output<bool, object>> TryRunExtensionMethodAsync(string methodName, object[] parameters)
132+
{
133+
Func<object[], object> func = ExtensionMethodToRun(methodName, parameters) as Func<object[], object>;
134+
135+
// Try calling the method
136+
try
137+
{
138+
if (func == null)
139+
return new Output<bool, object> { Item1 = false, Item2 = null };
140+
else
141+
return new Output<bool, object> { Item1 = true, Item2 = await (func(parameters) as dynamic) };
142+
}
143+
catch (Exception e)
144+
{
145+
BH.Engine.Base.Compute.RecordError($"Failed to run {methodName} extension method.\nError: {e.Message}");
146+
return new Output<bool, object> { Item1 = false, Item2 = null };
147+
}
148+
}
149+
150+
/***************************************************/
151+
152+
[Description("Finds an extension method with given name and parameters. For performance reasons compiles the method to a function the first time it is run, then stores it for subsequent calls.")]
153+
private static Func<object[], object> ExtensionMethodToRun(string methodName, object[] parameters)
154+
{
155+
if (parameters == null || parameters.Length == 0 || parameters.Any(x => x == null) || string.IsNullOrWhiteSpace(methodName))
156+
return null;
84157

85158
//Construct key used to store/extract method
86159
string key = methodName + string.Join("", parameters.Select(x => x.GetType().ToString()));
87-
160+
88161
// If the method has been called before, use the previously compiled function
89162
Func<object[], object> func;
90163
if (FunctionPreviouslyCompiled(key))
@@ -110,7 +183,7 @@ private static bool TryRunExtensionMethod(string methodName, object[] parameters
110183
{
111184
//Check that the parameter has a default value.
112185
//This should always be true, based on the logic of the ExtensionMethodToCall but additional check here for saftey
113-
if (parameterInfo[i].HasDefaultValue)
186+
if (parameterInfo[i].HasDefaultValue)
114187
{
115188
defaultArgs.Add(parameterInfo[i].DefaultValue); //Add the argument to be used when calling the function
116189
}
@@ -128,30 +201,11 @@ private static bool TryRunExtensionMethod(string methodName, object[] parameters
128201
else
129202
func = methodFunc; //Provided parameter count matches the parameters of the method -> set func to the compiled function of the method
130203
}
131-
204+
132205
StoreCompiledFunction(key, func);
133206
}
134207

135-
// Try calling the method
136-
try
137-
{
138-
if (func == null)
139-
{
140-
result = null;
141-
return false;
142-
}
143-
else
144-
{
145-
result = func(parameters);
146-
return true;
147-
}
148-
}
149-
catch (Exception e)
150-
{
151-
BH.Engine.Base.Compute.RecordError($"Failed to run {methodName} extension method.\nError: {e.Message}");
152-
result = null;
153-
return false;
154-
}
208+
return func;
155209
}
156210

157211
/***************************************************/

0 commit comments

Comments
 (0)