C# Pass any method as parameter - c#

I'm making a networking application where I want to implement strongly typed RPC. As result I'd like to be able to pass methods no matter the parameters so I can get them and store them in a dictionary so I can construct request parameters properly as well once a packet arrive I am able to read it using the parameters the same remote method used
I'd like something like this:
Register(Enum key, [method with unknown parameters])

In C# 8.0 using generics and Delegate Constraint you can do something like this:
using System;
using System.Collections.Generic;
namespace ConsoleApp
{
public class Program
{
public static void Main(params string[] args)
{
var app = new NetworkingApplication();
app.Register<Action<int, string>>(PacketType.Type1, Method1, 1, "string1 argument");
app.Register<Func<string, string>>(PacketType.Type2, Method2, "string2 argument");
app.OnPacketReceived(PacketType.Type1);
app.OnPacketReceived(PacketType.Type2);
}
public static void Method1(int arg1, string arg2)
{
Console.WriteLine($"Method1 Invoked with args: {arg1}, {arg2}");
}
public static string Method2(string arg1)
{
Console.WriteLine($"Method2 Invoked with args: {arg1}");
return "Foo";
}
}
public class NetworkingApplication
{
private readonly IDictionary<PacketType, DelegateInvoker> _registrations;
public NetworkingApplication()
{
_registrations = new Dictionary<PacketType, DelegateInvoker>();
}
public void Register<TDelegate>(PacketType packetType, TDelegate #delegate, params object[] args)
where TDelegate : Delegate
{
_registrations[packetType] = new DelegateInvoker(#delegate, args);
}
//invoke this when the packet is received
public void OnPacketReceived(PacketType type)
{
if (_registrations.TryGetValue(type, out var invoker))
{
invoker.Invoke();
}
}
private class DelegateInvoker
{
public DelegateInvoker(Delegate #delegate, object[] args)
{
Delegate = #delegate;
Arguments = args;
}
private Delegate Delegate { get; }
private object[] Arguments { get; }
public void Invoke()
{
Delegate.Method.Invoke(Delegate.Target, Arguments);
}
}
}
public enum PacketType
{
Type1,
Type2,
Type3
}
}

As mentioned above you can use MethodInfo, it belongs to the System.Reflection namespace. To do this, first get the Type type of the object like this:
var type = obj.GetType()
After this you can use var methods = type.GetMethods(). This will give you an MethodInfo[]. Search the element, using your favorite method for doing so. Such as Linq:
var method = methods.Where(it => it.Name == __yourName__).LastOrDefault();
*where yourName is the name of your method.
Now you have the method you are looking for. Get the parameters using
var parameters = method.getParameters();
And there are the parameters as ParameterInfo[].
From there you can get the type of each parameter using the parameter.ParameterType property.
This being said be very careful with Reflection, it is very, very powerful but can decrease performance heavily, when overused.
Have look at it System.Reflection namespace here .
You can now add the method to a collection, such as a dictionary:
var dictionary = new Dictionary<int,MethodInfo>();
dictionary.Add(1, method);
And retrieve it like this:
var method = dictionary[1];
To call the function you can user method.Invoke() and pass in the parameters as need.
EDIT:
If you would like to send the parameters as well as the function over a network. You could create a new class that serves as a DTO Data Transfer Object. This class could have as property an Array of parameters (ParameterInfo[]), the MethodInfo and anything else you want.
You could then serialize the object (maybe json) and send it to another system, that could then deserialize it, and Invoke the MethodInfo obj

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
public class Program
{
public static void Main(params string[] args)
{
var app = new NetworkingApplication();
app.Register(PacketType.Type1, () =>
{
Console.WriteLine("Type1 Packet is received!");
});
}
}
public class NetworkingApplication
{
private readonly IDictionary<PacketType, Action> _registrations;
public NetworkingApplication()
{
_registrations = new Dictionary<PacketType, Action>();
}
public void Register(PacketType packetType, Action method)
{
_registrations[packetType] = method;
}
//invoke this when the packet is received
public void OnPacketReceived(PacketType type)
{
if (_registrations.TryGetValue(type, out var action))
{
action?.Invoke();
}
}
}
public enum PacketType
{
Type1,Type2,Type3
}
}

Related

XUnit: Generalized tests

I usually create a test class for each method of the SUT to test. Currently, I have the problem that I write a lot of tests that are code duplication.
Checking if the method throws if the object was disposed or async disposed
Checking if the method throws if any argument is null
There are around 6-7 tests each time to check this. In every class for every method of any SUT. Currently, I have 150+ tests to test the same thing and they keep getting more. I just want to have a base class containing all these tests and I just want to define the test data to use.
Something like this: (does not work)
public class TestBase<TSut> where TSut : IDisposable {
protected TSut Sut { get; set; }
protected Delegate MethodToTest { get; set; }
protected IEnumerable<object?[]> DefaultParameters { get; set; } // Should be valid data
protected IEnumerable<object?[]> NullCheckingParameters { get; set; } // Every set should contain valid data except for one null value
[Theory]
[MemberData(nameof(DefaultParameters))]
public void TestMethod_ShouldThrowException_WhenObjectWasDisposed(object?[] data) {
this.Sut.Dispose();
Assert.Throws<ObjectDisposedException>(
() => this.MethodToTest.Method.Invoke(this.Sut, data)
);
}
[Theory]
[MemberData(nameof(NullCheckingParameters))]
public void TestMethod_ShouldThrowException_WhenParameterWasNull(object?[] data) {
this.Sut.Dispose();
Assert.Throws<ArgumentNullException>(
() => this.MethodToTest.Method.Invoke(this.Sut, data)
);
}
}
public class MethodTests : TestBase<MySut> {
public MethodTests() {
this.Sut = new MySut();
this.MethodToTest = this.Sut.MethodToTest;
this.DefaultParameters = new[] {
new object?[] {"Valid1", "Valid2", "Valid3"}
};
this.NullCheckingParameters = new[] {
new object?[] {null, "Valid2", "Valid3"},
new object?[] {"Valid1", null, "Valid3"},
new object?[] {"Valid1", "Valid2", null}
};
}
}
The problem is that the MemberData has to be a static member. So is there a way to generalize these tests?
In Xunit, the InlineData, MemberData and ClassData are all derived from the abstract class DataAttribute which is basically just this:
{
//
// Summary:
// Abstract attribute which represents a data source for a data theory. Data source
// providers derive from this attribute and implement GetData to return the data
// for the theory. Caution: the property is completely enumerated by .ToList() before
// any test is run. Hence it should return independent object sets.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
[DataDiscoverer("Xunit.Sdk.DataDiscoverer", "xunit.core")]
public abstract class DataAttribute : Attribute
{
protected DataAttribute();
//
// Summary:
// Marks all test cases generated by this data source as skipped.
public virtual string Skip { get; set; }
//
// Summary:
// Returns the data to be used to test the theory.
//
// Parameters:
// testMethod:
// The method that is being tested
//
// Returns:
// One or more sets of theory data. Each invocation of the test method is represented
// by a single object array.
public abstract IEnumerable<object[]> GetData(MethodInfo testMethod);
}
}
You can derive your own classes from this to return any sets of data you want.
For your first point, this class returns all the public methods in a class under test. It returns them as string's because XUnit won't enumerate generic object types in the test explorer window. The class provides an easy way to get the method info back from the string with method getMethodFromName
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SO74727807_xunit_generalized_tests
{
public class GenericPublicMethodsDataAttribute : Xunit.Sdk.DataAttribute
{
private Type objectType;
private MethodInfo[] methodInfos;
private Dictionary<string, MethodInfo> index = new Dictionary<string, MethodInfo>();
public GenericPublicMethodsDataAttribute(Type objectType_)
{
objectType = objectType_ ?? throw new ArgumentNullException($"Parameter {nameof(objectType_)} is null.");
methodInfos = objectType.GetMethods();
foreach (MethodInfo methodInfo in methodInfos)
{
string key = methodInfo.ToString();
index.Add(key, methodInfo);
}
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
foreach (string key in index.Keys)
{
string[] methodKey = new string[] { key };
yield return methodKey;
}
}
public MethodInfo getMethodFromName(string methodName)
{
if (index.TryGetValue(methodName, out MethodInfo info))
{
return info;
}
throw new ArgumentException($"No method info found for method name \"{methodName}\"");
}
}
}
For your second point, for example, this class would return sets of valid data with one null value on each iteration, like this:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SO74727807_xunit_generalized_tests
{
public class GenericNullParameterDataAttribute : Xunit.Sdk.DataAttribute
{
private object[] validData;
public GenericNullParameterDataAttribute(params object[] validData_)
{
validData = new object[validData_.Length];
Array.Copy(validData_, validData, validData.Length);
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
ParameterInfo[] parameters = testMethod.GetParameters();
// TODO: check that the object types in the valid data match the test method parameter types.
for (int i = 0; i < validData.Length; i++)
{
// Skip value types, they're not nullable
if (!parameters[i].ParameterType.IsValueType)
{
object[] methodData = new object[validData.Length];
Array.Copy(validData, methodData, validData.Length);
methodData[i] = null;
yield return methodData;
}
}
}
}
}
The method skips over value types since they are not nullable.
I discovered that it is important to make a new object array for each yield instead of trying to reuse the same one-- that confused the test discoverer.
To demonstrate, I made a small "class under test"
using System;
namespace SO74727807_xunit_generalized_tests
{
class ClassUnderTest : IDisposable
{
private bool disposedValue;
public void BadMethod(string param1, string param2, string param3)
{
// Doesn't throw on call in disposed object
// Doesn't throw on call with null parameters
// Doesn't do anything
}
public void GoodMethod(string param1, string param2, int param3)
{
// Throws if disposed
if (disposedValue) throw new ObjectDisposedException(this.ToString());
// Throws on null arguments
if (param1 == null)
{
throw new ArgumentNullException(nameof(param1));
}
if (param2 == null)
{
throw new ArgumentNullException(nameof(param2));
}
// int is a non-nullable type.
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
disposedValue = true;
}
else
{
throw new ObjectDisposedException(this.ToString());
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
Then you can write your generic unit tests like this:
using System;
using System.Reflection;
using Xunit;
namespace SO74727807_xunit_generalized_tests
{
public class GenericUnitTests
{
[Theory]
[GenericNullParameterData("Valid1", "Valid2", 3)]
public void GoodMethodThrowsOnNullArg(string param1, string param2, int param3)
{
ClassUnderTest sut = new ClassUnderTest();
Assert.Throws<ArgumentNullException>(() => sut.GoodMethod(param1, param2, param3));
}
[Theory]
[GenericNullParameterData("Valid1", "Valid2", "Valid3")]
public void BadMethodThrowsOnNullArg(string param1, string param2, string param3)
{
ClassUnderTest sut = new ClassUnderTest();
Assert.Throws<ArgumentNullException>(() => sut.BadMethod(param1, param2, param3));
}
[Theory]
[GenericPublicMethodsData(typeof(ClassUnderTest))]
public void PublicMethodsThrowOnDisposedObject(string methodName)
{
// Get a reference to the data provider attribute object
MethodBase method = MethodBase.GetCurrentMethod();
GenericPublicMethodsDataAttribute attr = (GenericPublicMethodsDataAttribute)method.GetCustomAttributes(typeof(GenericPublicMethodsDataAttribute), true)[0];
// Now we can get the method info
MethodInfo methodInfo = attr.getMethodFromName(methodName);
// Make default parameters
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
object[] args = new object[parameterInfos.Length];
for (int i = 0; i < parameterInfos.Length; i++)
{
args[i] = getDefault(parameterInfos[i].ParameterType);
}
// Now make the object under test and dispose it
ClassUnderTest cut = new ClassUnderTest();
cut.Dispose();
// Methods in disposed objects should throw
// Note that the ObjectDisposedException will be an inner exception,
// the actual type thrown is System.Reflection.TargetInvocationException
Assert.ThrowsAny<Exception>(() => methodInfo.Invoke(cut, args));
}
private static object getDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
[Fact]
public void testGenericPublicMethodsDataAttribute()
{
var data = new GenericPublicMethodsDataAttribute(typeof(ClassUnderTest));
Assert.NotNull(data);
}
}
}
The tests are shown in the Test Explorer like this:
We can see that the good method passes them and the bad method fails.
The inherited methods like ToString() and GetHashCode() also fail--they do not throw on disposed objects. You could modify the GenericPublicMethodsDataAttribute class to skip the inherited methods by checking the DeclaringType property of the method info.

How can i get the parameter values of an anonymous method passed as a func?

I am calling methods on a remote system. The remote system implements an interface that both systems have a copy of (via shared nuget repository). At the moment i am sending the requests like this:
var oldRequest = new FooRequest("GetEmployeeById", new object[] { "myPartner", 42, DateTime.Now.AddDays(-1) });
Here is the interface:
public class FooResponse<T> { }
public interface IFooController
{
FooResponse<string> GetEmployeeById(string partnerName, int employeeId, DateTime? ifModifiedSince);
}
As you can image, sometimes programmers passes arguments in the wrong order to the array in the constructor, and things start to fail. To resolve this I have created the following code to have intellisense support when creating the FooRequest:
public static FooRequest Create<T>(Func<FooResponse<T>> func)
{
return new FooRequest(null, null); // Here goes some magic reflection stuff instead of null.
}
It is now possible to create a FooRequest like this:
public static IFooController iFooController => (IFooController)new object();
public static FooRequest CreateRequest<T>(Func<FooResponse<T>> func)
{
return FooRequest.Create(func);
}
var newRequest = CreateRequest(() => iFooController.GetEmployeeById("myPartner", 42, DateTime.Now.AddDays(-1)));
My question then is: How will i be able to get the name of the method and the value of the parameters in the FooRequest.Create-method?
I have exhausted both my reflection and google-skills trying to find the values, but no luck so far.
Complete compiling code can be found here if someone wants to give it a shot: http://ideone.com/ovWseI
Here is a sketch of how you can do this with expressions:
public class Test {
public static IFooController iFooController => (IFooController) new object();
public static FooRequest CreateRequest<T>(Expression<Func<FooResponse<T>>> func) {
return FooRequest.Create(func);
}
public static void Main() {
var newRequest = CreateRequest(() => iFooController.GetEmployeeById("myPartner", 42, DateTime.Now.AddDays(-1)));
Console.ReadKey();
}
}
public class FooRequest {
public static FooRequest Create<T>(Expression<Func<FooResponse<T>>> func) {
var call = (MethodCallExpression) func.Body;
var arguments = new List<object>();
foreach (var arg in call.Arguments) {
var constant = arg as ConstantExpression;
if (constant != null) {
arguments.Add(constant.Value);
}
else {
var evaled = Expression.Lambda(arg).Compile().DynamicInvoke();
arguments.Add(evaled);
}
}
return new FooRequest(call.Method.Name, arguments.ToArray());
}
public FooRequest(string function, object[] data = null) {
//SendRequestToServiceBus(function, data);
Console.Write($"Function name: {function}");
}
}
public class FooResponse<T> {
}
public interface IFooController {
FooResponse<string> GetEmployeeById(string partnerName, int employeeId, DateTime? ifModifiedSince);
}

Passing Parameters in reflections Windows store Apps

I want to pass some parameters to function that is being called using reflection in a universal windows App. Below is the code which I tried and I am getting an exception "Parameters Count mismatch.". please advice me.
public class myClass
{
public async void btn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Type type = Type.GetType("moto_Windows.Actions.NextViewAction");
object[] mParam = new object[] { 5, 10 };
var nva = Activator.CreateInstance(type);
await (dynamic)type.GetTypeInfo().GetDeclaredMethod("NextView").Invoke(nva, mParam);
}
}
The class I am trying to invoke look like below
namespace moto_Windows.Actions
{
public class NextViewAction
{
public NextViewAction(object [] obj)
{
//Constructor
}
public async void NextView()
{
//Method to be invoked.
}
}
}
Finally I solved this by passing values in a dictionary through a contructor, my code is
Type type = Type.GetType("moto.Actions." + ctrl.Action.name);
ctrl = (Carrot_Control)btn.DataContext;
Dictionary<string, object> _dict = new Dictionary<string, object>();
dict.Add("a", 5);
_dict.Add("b", 10);
var t = Activator.CreateInstance(type, _dict);
await (dynamic)type.GetTypeInfo().GetDeclaredMethod("NextView").Invoke(t, null);
and the class i tried to invoke
namespace moto_Windows.Actions
{
public class NextViewAction
{
public NextViewAction(Dictionary<string,object> _dict)
{
//Constructor
string a = _dict[a];
string b = _dict[b];
}
public async void NextView()
{
//Method to be invoked.
}
}
}
please post if anybody has better answer.

Can't access the class I just created

I hope this is a simple question. I'm building a simple console application in C#. I have a class:
using System;
using Filter;
public class Params
{
public string key;
public bool distinct;
public List<string> fields;
public string filter;
public int limit;
public int skip;
public bool total;
public List<Tuple<string, GroupType>> group;
public List<Tuple<string, OrderType>> order;
public Params()
{
key = "";
distinct = false;
fields = new List<string>();
filter = "";
group = new List<Tuple<string, GroupType>>();
limit = 0;
order = new List<Tuple<string, OrderType>>();
skip = 0;
total = false;
}
public void AddGroup(string field, GroupType type)
{
group.Add(new Tuple<string, GroupType>(field, type));
}
public void AddOrder(string field, OrderType type)
{
order.Add(new Tuple<string, OrderType>(field, type));
}
}
My program .cs class is:
namespace csharpExample
{
class Program
{
public static void Main(string[] args)
{
Params p = new Params();
Console.WriteLine("Test");
}
}
}
I want to use Params in my program.cs class where Main() is called. I thought I could simply use Params like above. I've also tried to do a using Params; both of these are errors in VS since it can't find the directive. I've also tried adding my own namespace: namespace MyNameSpace; around my Params class. When I do this I still am unable to do a using MyNameSpace; statement as it can't find it.
I just want to extract out a bunch of functions into a class that I can reuse. How do i call this class once it's created?
-Thanks
Thanks for the help.
If you want to access the Params object in the Main function, just add Params p = new Params (); to the Main function at the top.
Most likely your problem is that Main is static, meaning that it can't access other things that aren't static which are outside of it. If you declared Params in the Program class, unless you made it static, it can't be accessed in Main.
Are you talking about calling the constructor or the properties you are setting? You can set the class at the top of your base class and then call the instance of it. But since it is a static class you should probably use a helper method in the main.
namespace Example
{
public class Program
{
Params p = new Params();
string writefromParams() // I exist just to give the string back from params with a nonstatic method
{
return p.key;
}
static void Main(string[] args)
{
Program p2 = new Program(); // set up a new instance of this very class
Console.WriteLine(p2.writefromParams()); // get non static method from class
Console.ReadLine();
}
}
}

action delegate. How to get methods infos called in delegate?

I need to get MethodInfo for method called in Action delegate in order to check, whether methods called in Action has MyCustomAttibute
public void Foo( Action action )
{
if(Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0)
{
throw new ArgumentException("Invalid action");
}
}
The Foo method should be able to be called as following:
Foo(() =>
{
instanceOfFooClass.Method1().Method2();
});
In Foo method I want to be sure that Method1 and Method2 has MyCustomAttribute. However action.Method is giving me the MethodInfo, which is the action of delegate, which happens when using lambda expression. Is there any way to get Method1 and Method2 MethodInfo?
As mentioned in the comments, Expression<T> is probably the best way to achieve this. However, it requires a Compile() at runtime so it should be performance profiled.
With Expression<T> you can easily get access to Method info like this:
public MethodInfo GetMethodInfo(Expression<Action> action)
{
return ((MethodCallExpression)action.Body).Method;
}
But, before executing the action you must do this:
private void InvokeMethod(Expression<Action> action)
{
action.Compile().Invoke();
}
EDIT
Ah yes, I forgot how to get access to the customer attribute. You would do it like this:
var methodInfo = ((MethodCallExpression)myAction.Body).Method;
var attributes = methodInfo.GetCustomAttributes<T>(true);
EXAMPLE
Here is an example showing passing chained method calls to Expression<Action>:
public class ActionTest
{
public void DoAction(Action action)
{
action();
}
public void DoExpressionAction(Expression<Action> action)
{
var method2Info = ((MethodCallExpression)action.Body).Method;
// a little recursion needed here
var method1Info = ((MethodCallExpression)((MethodCallExpression)action.Body).Object).Method;
var myattributes2 = method2Info.GetCustomAttributes(typeof(MyAttribute), true);
var myattributes1 = method1Info.GetCustomAttributes(typeof(MyAttribute), true);
action.Compile().Invoke();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
private string message;
public MyAttribute(string message)
{
this.message = message;
}
}
public class MethodTest
{
[MyAttribute("Number1")]
public MethodTest Method1()
{
Console.WriteLine("Action");
return this;
}
[MyAttribute("Number2")]
public MethodTest Method2()
{
Console.WriteLine("ExpressionAction");
return this;
}
}
class Program
{
static void Main(string[] args)
{
ActionTest target = new ActionTest();
MethodTest instance = new MethodTest();
target.DoExpressionAction(() => instance.Method1().Method2() );
Console.ReadLine();
}
static void Method1()
{
Console.WriteLine("Action");
}
static void Method2()
{
Console.WriteLine("ExpressionAction");
}
}
If you call your Foo() methdod like this:
Foo(instanceOfFooClass.Method);
Your code works as you'd expect (void methods are actions, after all).
On a side note, I think "chaining" method calls in fact counts as you're only passing the last one through.
Full sample demonstrating the behavior:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
class MyCustomAttribute : Attribute { }
class FooClass
{
[MyCustom]
public void DecoratedMethod() { Console.WriteLine("Decorated Method - executed."); }
public void NotDecoratedMethod() { Console.WriteLine("Not Decoreated Method - executed."); }
}
class Program
{
static void Main(string[] args)
{
FooClass instanceOfFooClass = new FooClass();
Foo(instanceOfFooClass.DecoratedMethod);
Foo(instanceOfFooClass.NotDecoratedMethod);
Console.ReadLine();
}
public static void Foo(Action action)
{
if (Attribute.GetCustomAttributes(action.Method, typeof(MyCustomAttribute)).Count() == 0)
Console.WriteLine(string.Format("Invalid method {0}", action.Method.Name));
else
{
Console.WriteLine(string.Format("Valid method {0}", action.Method.Name));
action.Invoke();
}
}
}
}

Categories