Refactor methods invoking delegates with different parameter signatures - c#

Is there a pattern that I could apply to refactor this code? The only difference between the two methods is that that one method takes an extra parameter and passes it to the delegate?
I found out that delegates cannot take overloaded method signatures. How could I add one more level of indirection? :)
public static void ProcessFolder(
ProcessFolderDelegate processFolderDelegate
)
{
using (var esb = ExchangeService.GetExchangeServiceBinding())
{
var contactFolder = FolderService.GetPublicFolder(esb,
Properties.Settings.Default.ExchangePublicFolderName);
processFolderDelegate(esb, contactFolder);
}
}
public static void ProcessContact(
ProcessContactDelegate processContactDelegate,
Contact contact //extra param
)
{
using (var esb = ExchangeService.GetExchangeServiceBinding())
{
var contactFolder = FolderService.GetPublicFolder(esb,
Properties.Settings.Default.ExchangePublicFolderName);
processContactDelegate(esb, contactFolder, contact); //extra param
}
}

public delegate void Action(TYPE_OF_ESB esb, TYPE_OF_CONTACT_FOLDER contact folder);
private static void Process(Action action)
{
using (var esb = ExchangeService.GetExchangeServiceBinding())
{
var contactFolder = FolderService.GetPublicFolder(esb, Properties.Settings.Default.ExchangePublicFolderName);
action(esb, contactfolder);
}
}
Process((esb, contactfolder)=>processFolderDelegate(esb, contactFolder));
Process((esb, contactfolder)=>processContactDelegate(esb, contactFolder, contact));

No, the code you posted is actually a very terse method of applying this patter. You're not going to find a terser way unless there is some way of abstracting the type of method being called, or by removing all type safety, which I would not recommend

public static void ProcessFolder(ProcessFolderDelegate del)
{
Process((b, f) => del(b, f));
}
public static void ProcessContact(ProcessContactDelegate del, Contact contact)
{
Process((b, f) => del(b, f, contact));
}
private static void Process(
Action<ExchangeServiceBinding, ContactsFolderType> action)
{
// i've guessed that esb is of type ExchangeServiceBinding
// and that contactFolder is of type ContactsFolderType
// if that's not the case then change the type parameters
// of the Action delegate in the method signature above
using (var esb = ExchangeService.GetExchangeServiceBinding())
{
var contactFolder = FolderService.GetPublicFolder(
esb, Properties.Settings.Default.ExchangePublicFolderName);
action(esb, contactFolder);
}
}

You could use anonymous methods or lambdas like this:
delegate void ProcessDelegate<T>(T param);
.
.
public static void Process<T>(ProcessDelegate<T> processDelegate)
{
using (var esb = ExchangeService.GetExchangeServiceBinding())
{
var contactFolder = FolderService.GetPublicFolder(esb,
Properties.Settings.Default.ExchangePublicFolderName);
processDelegate(contactFolder);
}
}
and then call the method like this
Process(contactFolder => MyMethod(esb, contactFolder));
Process(contactFolder => MyMethod(esb, contactFolder, contact));
Where MyMethod is the actual method you're calling, so you contain it within your lambda expression rather than a delegate. Think something like that may work?

Related

C# Pass any method as parameter

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
}
}

Pass a method to another method

I want to be able to specify a method in another method.
Something like
public class Binder
{
public void Bind(whatShouldIWriteHere?)
{
// do stuff with the MethodInfo
}
}
so that I can do:
public class A
{
public void DoIt(string tmp)
{
}
}
var binder = new Binder()
binder.Bind<A>(x => x.DoIt);
Instead of:
var method = typeof(A).GetMethod("DoIt");
binder.Bind(method);
Is that possible? :)
Pass the method as a delegate and use the Delegate.Method property.
In your case Binder.Bind would be like:
public void Bind(Delegate del)
{
var info = del.Method;
//Add your logic here.
}
And to pass a method to it:
var binder = new Binder();
var instance = new A();
binder.Bind(new Action<string>(instance.DoIt))

How can I pass a collection of delegates to be run into a method

Referencing the sample below, is there a way to pass the work done by GetType1s() and GetType2s() in on this constructor? I'm looking for something along the lines of a List<Action> so that I can just run each action iteratively from within the constructor without caring about what it does, but I can't think of an appropriate way to do that in which variables are set (and of different types). I realize this is getting awfully close to DI and IOC, so maybe there's no way out of creating a container if I want to pass this work into the class like this?
public MainPageViewModel(string taskBaseLocation)
{
TasksBaseLocation = taskBaseLocation;
Type1List = TasksModel.GetType1s(TasksBaseLocation);
Type2List = TasksModel.GetType2s(TasksBaseLocation);
}
Declare an appropriate delegate type and pass that in a generic list.
public delegate retType MyDelegate(param1type param1, param2type param2,...)
List<MyDelegate> listToPass = new List<MyDelegate>();
Since delegates are multicast, just pass an Action:
if(action != null) { action(); }
and combine at the caller.
If you need to run them independently (for example, to run later actions even if earlier actions fail), de-construct it:
if(action != null)
{
foreach(Action child in action.GetInvocationList())
{
try
{
child();
}
catch (Exception ex)
{
// handle/log etc
}
}
}
Note, however, that this doesn't magically provide access to the member variables from the delegate, unless the delegate's context already had access to the fields (and a reference to the new instance, perhaps via Action<T> instead of Action)
I have two approaches here, using pure lambdas or using Delegate.Combine. I'd prefer the lambda variety.
using System;
using System.Linq;
namespace X
{
public class MyType
{
public delegate void DoInitialize(MyType instance);
public int a,b,c;
public MyType(DoInitialize initialize)
{
initialize(this);
}
public MyType(params Action<MyType>[] initializers)
{
foreach (var initializer in initializers)
initializer(this);
}
public static void LambdaApproach()
{
var instance = new MyType(
i => i.a = 1,
i => i.b = 2,
i => i.c = 42);
Console.WriteLine("{0}, {1}, {2}", instance.a, instance.b, instance.c);
}
public static void MulticastApproach()
{
var lambdas = new DoInitialize[] {
i => i.a = 1,
i => i.b = 2,
i => i.c = 42, };
var instance = new MyType((DoInitialize) Delegate.Combine(lambdas.ToArray()));
Console.WriteLine("{0}, {1}, {2}", instance.a, instance.b, instance.c);
}
public static void Main(string[] args)
{
LambdaApproach();
MulticastApproach();
}
}
}

Execute a function from a variable in C#

Is there any way so that you can call a function with a variable?
variable+"()";
or something like that, or would I have to use if statements?
A switch seems like it might be the answer, so if the variable's value=var1 I want it to execute var1(); if the value is var2 I want it to execute var2(); how would I code it?
Basically, I am trying to find a cleaner alternative to
if (variable == var1)
{
var1();
}
if (variable == var2)
{
var2();
}
It would be possible to use reflection to find a method in an object and call that method, but the simplest and fastest would be to simply use a switch:
switch (variable) {
case "OneMethod": OneMethod(); break;
case "OtherMethod": OtherMethod(); break;
}
You could use Reflection http://msdn.microsoft.com/en-us/library/ms173183(v=vs.80).aspx to access any function or member by name. It takes some getting used to though. It also has performance issues, so if you can avoid using it, you should.
This is what delegates are for:
Action f = ()=>Console.WriteLine("foo");
f();
I assume using strings is not actually a requirement.
You can use delegates. MSDN: http://msdn.microsoft.com/en-us/library/900fyy8e(v=vs.71).aspx
Exa:
public delegate void TestDelegate();
class TestDelegate
{
public static void Test()
{
Console.WriteLine("In Test");
}
public static void Main()
{
TestDelegate testDelegate = new TestDelegate(Test);
testDelegate();
}
}
You can use the MethodInfo class
Type yourtype = yourObject.GetType();
MethodInfo method = yourtype.GetMethod(variable);
var result = method.Invoke(yourObject,null);
string className = "My.Program.CoolClass"; //including namespace
string method= "Execute";
var type = Type.GetType(className);
var method = type.GetMethod(method);
method.Invoke(classObj, null);
Check out this post.
Use reflection.
http://dotnetslackers.com/Community/blogs/haissam/archive/2007/07/25/Call-a-function-using-Reflection.aspx
You can use MethodMethodInfo.
http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.aspx
http://msdn.microsoft.com/en-us/library/a89hcwhh.aspx
Here's a sample how you can call a method via reflection:
public class MyClass
{
public void PrintHello()
{
Console.WriteLine("Hello World");
}
}
//...
public void InvokeMethod(object obj, string method)
{
// call the method
obj.GetType().GetMethod(method).Invoke(obj, null);
}
//...
var o = new MyClass();
var method = "PrintHello";
//...
InvokeMethod(o, method);
(I will complete #Matthew's excellent answer):
var x = (Action) ( ()=>Print("foo") );
x();
p.s. you can fully variable names too:
private Dictionary<string, dynamic> my = new Dictionary<string, dynamic>();
my["x"] = .....
my["x"]();
public class FunctionTest
{
void Main()
{
Action doSomething;
doSomething = FirstFunction;
doSomething();
doSomething = SecondFunction;
doSomething();
}
void FirstFunction()
{
Console.Write("Hello, ");
}
void SecondFunction()
{
Console.Write("World!\n");
}
}
output:
Hello, World!
Doesn't get too much simpler than that.

Returning a function pointer

I'm trying to create a url builder similar to the one in asp mvc except our methods are frequently changing parameters and breaking pages.
Does anyone know if it's possible to coerce c# into allowing event like syntax to be returned from a delegate like this:
new UrlBuilder2<FakeController>(x => { return x.ActionWithInt; });
The class would be similar to this:
public class UrlBuilder<TController>
{
public UrlBuilder2(Func<TController, TType> action)
{
}
}
Basically I want to know what Type to use for TType. Or if it's at all possible.
Edit -
I would (if possible) like to use just the method, similar to how you would assign an event ( clickEvent =+ myMethod;)
Not exactly sure what you want to achieve, but assuming you want to generate link simlar to this:
MyForm/MyMethod.aspx
based on WebForm (or any other class) like this:
public class MyForm {
public void MyMethod() {
// Something here
}
public void MethodWithParams(int i, string str) {
// Something here
}
}
You can use this builder (test included):
class UrlBuilder2<T> {
private readonly Expression<Func<T, object>> callExpression;
public UrlBuilder2(Expression<Func<T,object>> callExpression) {
this.callExpression = callExpression;
}
public override string ToString() {
MethodCallExpression call = (MethodCallExpression) callExpression.Body;
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}/{1}.aspx", call.Object.Type.Name, call.Method.Name);
var delimiter = "?";
var formalParams = call.Method.GetParameters();
for (int i = 0; i < formalParams.Length; i++) {
var actual = call.Arguments[i];
if (actual == null)
continue; // Do not put NULL to QueryString
var formal = formalParams[i].Name;
sb.AppendFormat("{0}{1}={2}", delimiter, formal, HttpUtility.HtmlEncode(actual.ToString()));
}
return sb.ToString();
}
}
[Test]
public void CanBuildUrlByClassAndMethodName() {
var str = new UrlBuilder2<MyForm>(c => c.MyMethod()).ToString();
str.Should().Be.EqualTo("MyForm/MyMethod.aspx");
}
[Test]
public void CanBuildUrlByMethodWithParams() {
var str = new UrlBuilder2<MyForm>(c => c.MethodWithParams(2, "hello")).ToString();
str.Should().Be.EqualTo("MyForm/MyMethod.aspx?i=2&str=hello");
}
All this will allow you to keep the links type-safe and refactoring advantages will be leveraged.
You will probably need to enhance the UrlBuilder2 but this should get you started.
If you just want to use name of a method to generate links you can do something like this:
class MyClass {
public void MyMethod() {}
}
class UrlBuilder3<T> {
Expression<Func<T, Action>> info;
public UrlBuilder3(Expression<Func<T, Action>> info) {
this.info = info;
}
public override string ToString() {
UnaryExpression exp = (UnaryExpression)info.Body;
MethodCallExpression createDelegate = (MethodCallExpression)exp.Operand;
// 0-Action,1-x,2-Delegate as Constant
ConstantExpression methodArgument = (ConstantExpression)createDelegate.Arguments[2];
MethodInfo method = (MethodInfo)methodArgument.Value;
return string.Format("{0}/{1}.aspx", typeof(T).Name, method.Name);
}
}
[Test]
public void UrlByDelegate() {
new UrlBuilder3<MyClass>(x => x.MyMethod).ToString()
.Should().Be.EqualTo("MyClass/MyMethod.aspx");
}
The tricky thing is correctly resolving the Expression tree. The code above works for this particular sample, but you will need to check it works for all your cases.
You can return a function pointer aka a delegate in c# as below.
public delegate int mydelegate(string s);
public class Test
{
mydelegate MyFunc(string s)
{
return (astring => astring.Length + s.Length);
}
}
This would allow you to attach the output of the function to an event.
var test = new Test();
someevent += test.MyFunc("this is a test");
Assuming that someevent took a function with the same signature as the delegate.

Categories