Send static method + parameter as parameter - c#

I'd like to do something like this, but it's not possible.(Cann't convert from 'void' to 'System.Action').
class Program
{
public static void Main()
{
int n = 2;
ClassB cb = new ClassB();
cb.SetMethod(ClassA.MethodA(n)); //Cann't convert 'void' to 'System.Action<int>'
}
}
public class ClassA
{
public static void MethodA(int a)
{
//code
}
}
public class ClassB
{
Delegate del;
public void SetMethod(Action<int> action)
{
del = new Delegate(action);
}
public void ButtonClick()
{
del.Invoke();
}
}
public delegate void Delegate(int n);
I can send the argument "n", as second argument in the "setMethod" method, but I would have to store a variable to after pass to "del.Invoke(PARAM)". I'd like to use "del.Invoke()".

You seem to have a misunderstanding of delegates. Delegates represent methods, not method calls. If you supply arguments to a method, it becomes a method call. So here:
cb.setMethod(ClassA.methodA(n));
ClassA.methodA(n) is a method call, and you can't assign that to a delegate.
Basically, you can't pass the parameter at this stage. You have to pass the parameter when you invoke the delegate. e.g.
del.Invoke(5);
But you said you want to always write del.Invoke(), with no arguments. Well, then you should not use an Action<int>, you should just use Action, which does not accept any parameters.
class Program
{
public static void Main()
{
int n = 2;
ClassB cb = new ClassB();
cb.setMethod(() => ClassA.methodA(n));
}
}
public class ClassA
{
public static void methodA(int a)
{
//code
}
}
public class ClassB
{
Delegate del;
public void setMethod(Action action)
{
del = new Delegate(action);
}
public void ButtonClick()
{
del.Invoke();
}
}
public delegate void Delegate();

cb.setMethod(new Action(ClassA.methodA));

It isn't clear whether you want to capture the integer at the call site (e.g. as a closure), or whether you intend passing in a parameter explicitly to the delegate.
Here's the former case, where the value is captured:
public static void Main()
{
var n = 2;
var cb = new ClassB();
cb.setMethod(() => ClassA.methodA(n));
}
The delegate is thus unaware of the captured variable, and is just defined as:
public delegate void Delegate();
If however you do intend passing the int at invoke time, then the value for the int needs to be passed in the ButtonClick:
public static void Main()
{
var cb = new ClassB();
cb.setMethod(ClassA.methodA);
}
public class ClassB
{
Delegate del;
public void setMethod(Action<int> action)
{
del = new Delegate(action);
}
public void ButtonClick()
{
var n = 2;
del.Invoke(n);
}
}
public delegate void Delegate(int n);
Edit - Re Do you think there's a better way
There's no real reason to explicitly require a delegate. Action and Func (and Action<int>, depending on the above) are already delegates. As an improvement, you should check that the action is assigned before invoking it. The null conditional operator will simplify this as _action?.Invoke(). But you can go one step further, and prevent the action from ever being unassigned by requiring it in the constructor:
public class ClassB
{
// Can be readonly if it is assigned only ever once, in the ctor.
private readonly Action _action;
public ClassB(Action action)
{
Contract.Assert(action != null);
_action = action;
}
public void ButtonClick()
{
_action(); // i.e. no need for Invoke or null check.
}
}

Related

How to use params of object in Action

While creating a UI system, I am trying to create a UI event handler that takes a variable length parameter and performs an action.
Here is my code
class UIEventTriggerManager : MonoBehaviour
{
public static UIEventTriggerManager Instance;
private void Awake()
{
Instance = this;
}
public void Publish(string key, params object[] args)
{
m_eventHandler[key]?.Invoke(args);
}
public void Subscribe(string eventName, Action<object[]> action)
{
if (m_eventHandler.ContainsKey(eventName) == false)
{
m_eventHandler.Add(eventName, action);
}
else
{
m_eventHandler[eventName] += action;
}
}
}
class CharacterUI
{
public void FirstOpen()
{
UIEventTriggerManager.Instance.Subscribe("ChagedCharacter", ChagedCharacter); // ChagedCharacter error
UIEventTriggerManager.Instance.Subscribe("ChagedCharacter2", ChagedCharacter2); // error
UIEventTriggerManager.Instance.Subscribe("ChagedCharacter3", ChagedCharacter3); // error
}
public void ChagedCharacter()
{
//....
}
public void ChagedCharacter2(int a)
{
//....
}
public void ChagedCharacter3(int a, string b, float c)
{
//....
}
}
How to use Subscribe 'ChangedCharacter' Method??
Do I have to add 'params object[] args' to the method argument??
I know syntax error but I need an event handler to manage multiple parameters.
I'm trying to implement it this way, please give me some advice.
The params does only exist in the signature of
Publish(string key, params object[] args)
meaning you can call this method with additional parameters - or not. Inside this method it is simply an object[] of variable length. If no args was passed in at all it will simply be an empty array.
Also yourself already seem to know that Action<object[]> simply takes an object[] - no params here.
Action<object[]> basically is nothing else than a shorthand for
public delegate void SomeName(object[] args);
Therefore also the listener method is simply
// Should it be "Changed" btw?
public void ChagedCharacter(object[] args)
and it is the responsibility of this (and any listener) to correctly handle the array.
The error should actually tell you exactly which signature ChagedCharacter was expected to have.
You could then e.g. do
private void ChagedCharacter(object[] args)
{
switch(args.Length)
{
case 0:
ChagedCharacter ();
break;
case 1 when args[0] is int intValue :
ChagedCharacter (intValue);
break;
case 3 when args[0] is int intValue && args[1] is string stringValue && args[2] is float floatValue:
ChagedCharacter (intValue, stringValue, floatValue);
break;
default:
break;
}
}
But honestly before you do that you rather overthink your design ...
Maybe this solution is enough for you. and this solution also have boxing unboxing , So maybe can use <T> to replace object[] to avoid.
public enum EventType
{
ChagedCharacter,
ChagedCharacter2
}
public class UIEventTriggerManager : MonoBehaviour
{
public static UIEventTriggerManager Instance;
private void Awake()
{
Instance = this;
}
static Dictionary<EventType, List<IReciveEvent>> recives = new Dictionary<EventType, List<IReciveEvent>>();
public void Publish(EventType key, params object[] args)
{
if (recives.ContainsKey(key))
{
foreach(IReciveEvent ir in recives[key])
{
ir.GetEvent(key,args);
}
}
}
public void Subscribe(EventType type , IReciveEvent ui)
{
if(!recives.ContainsKey(type))
{
recives[type] = new List<IReciveEvent>();
}
if(!recives[type].Contains(ui))
{
recives[type].Add(ui);
}
}
}
public interface IReciveEvent
{
void GetEvent(EventType type, object[] objs);
}
public class CharUI : MonoBehaviour, IReciveEvent
{
private void Awake()
{
UIEventTriggerManager.Instance.Subscribe(EventType.ChagedCharacter, this);
UIEventTriggerManager.Instance.Subscribe(EventType.ChagedCharacter2, this);
}
public void GetEvent(EventType type, object[] objs)
{
if(type == EventType.ChagedCharacter)
{
//todo
}
else if ( type == EventType.ChagedCharacter2)
{
int a = (int)objs[0];
//todo
}
//....
}
}

Could C# delegate point to another class/object's method?

Here, I wish to gain a delegate like function pointer, to point to a class method that's in another class (named Inner), and then pass it to a static function, like below:
public class Inner
{
public int M = 3;
public void F() { Console.WriteLine("f"); }
public void G() { Console.WriteLine("g"); }
}
class Program
{
public static void Caller(Action a)
{
a();
}
static void Main(string[] args)
{
var i = new Inner();
var f = i.F;
var g = i.G;
f();//error
g();//error
Program.Caller(f);
Console.WriteLine("Hello World!");
}
}
I'm from c/c++, and in c/c++, function pointer like this is very straight forward, but this C# code fail to compile. I googled and found almost all delegate explanations talks about delegate that points to class method inside itself.
My question is, how to fix the code to make it work?
Coding Seb's answer highlight's the cause of the issue, but doesn't really explain why.
It is because i.F is a method group, and not a specific method pointer. For example, imagine Inner was defined as so:
public class Inner
{
public void F() { Console.WriteLine("f"); }
public void F(string name) { Console.WriteLine(name); }
}
Which method would i.F refer to? F() or F(string)?
For that reason, you need to define the variable type explicitly, or cast the pointer:
Action f = i.F;
Or:
var f = (Action)i.F;
You can not set methods group in implicit variables in C# so if you just change 2 var in Action it's working
public class Inner
{
public int M = 3;
public void F() { Console.WriteLine("f"); }
public void G() { Console.WriteLine("g"); }
}
class Program
{
public static void Caller(Action a)
{
a();
}
static void Main(string[] args)
{
var i = new Inner();
Action f = i.F;
Action g = i.G;
f();
g();
Program.Caller(f);
Console.WriteLine("Hello World!");
}
}

C# callbacks into windows forms

I'm trying to create a class (in the context of a Windows Application) that can update progress (or send some user message) back to the main form UI via delegates. The problem I have is that the compiler won't allow any of the constructs I attempt because of missing object references. This has been discussed here but no answers had to do with writing to an object on a Form.
in c++ I would do this:
void LogToUI(string s)
{
if(Form1)
Form1->update(s);
}
void TForm1::update(string s)
{
listbox->Items->Add(s);
}
// so that any function, anywhere, can update the listbox (thread safety aside)
in C#:
namespace test
{
public delegate void callback(String msg);
public partial class Form1 : Form
{
public void writeToListbox(String s)
{
listbox.Items.Add(s);
}
public static void writeToConsole(String s)
{
System.Console.WriteLine(s);
}
public void createclass
{
callback ui_update = writeToConsole; // this is ok
callback ui_update = writeToListbox; // not allowed
someclass SC = new someclass(ui_update);
}
}
class someclass
{
callback cb;
void someclass(callback T)
{
this.cb = T;
}
void logthis(string s)
{
cb("it's me!");
}
}
}
I understand the problem with having to assign a static method to the delegate, and the Form1 method is non-static. I would like to use the delegate method because it seems the cleanest; I just can't find a way to write this in such a way as to make it work, short of passing a pointer back to the Form, which seems messy.
I believe I just came across the answer. You have to expose a static reference to a UI object, in this case a ListBox. Then you can assign the callback delegate to a function that makes sure the listbox reference is not null. You just need to make sure you assign the static reference when the form is created:
namespace test
{
public delegate void callback(String msg);
public partial class Form1 : Form
{
public static ListBox callbackListBox; // add this
public void writeToListbox(String s)
{
if(null == callbackListBox)return; // add this check
// also make this threadsafe:
if (callbackListBox.InvokeRequired)
{
callbackListBox.Invoke(new MethodInvoker(() => { writeToListbox(s); }));
}else{
callbackListBox.Items.Add(s);
callbackListBox.TopIndex = callbackListBox.Items.Count - (callbackListBox.Height / callbackListBox.ItemHeight);
}
}
public static void writeToConsole(String s)
{
System.Console.WriteLine(s);
}
public void createclass
{
callback ui_update = writeToListbox; // now OK
someclass SC = new someclass(ui_update);
}
// and add this to the form's constructor:
public Form1()
{
InitializeComponent();
callbackListBox = listbox1;
}
}
class someclass
{
callback cb;
void someclass(callback T)
{
this.cb = T;
}
void logthis(string s)
{
cb("it's me!");
}
}
}
I still have to try this, but at least the compiler is not complaining.

Issue creating a parameterized thread

I'm having problems trying to create a thread with a ParameterizedThreadStart. Here's the code I have now:
public class MyClass
{
public static void Foo(int x)
{
ParameterizedThreadStart p = new ParameterizedThreadStart(Bar); // no overload for Bar matches delegate ParameterizedThreadStart
Thread myThread = new Thread(p);
myThread.Start(x);
}
private static void Bar(int x)
{
// do work
}
}
I'm not really sure what I'm doing wrong since the examples I found online appear to be doing the same thing.
Frustratingly, the ParameterizedThreadStart delegate type has a signature accepting one object parameter.
You'd need to do something like this, basically:
// This will match ParameterizedThreadStart.
private static void Bar(object x)
{
Bar((int)x);
}
private static void Bar(int x)
{
// do work
}
This is what ParameterizedThreadStart looks like:
public delegate void ParameterizedThreadStart(object obj); // Accepts object
Here is your method:
private static void Bar(int x) // Accepts int
To make this work, change your method to:
private static void Bar(object obj)
{
int x = (int)obj;
// todo
}
It is expecting an object argument so you can pass any variable, then you have to cast it to the type you want:
private static void Bar(object o)
{
int x = (int)o;
// do work
}
You need to change Bar to
private static void Bar(object ox)
{
int x = (int)ox;
...
}
The function you pass to ParameterisedThreadStart needs to have 1 single parameter of type Object. Nothing else.
Method Bar should accept object parameter. You should cast to int inside. I would use lambdas here to avoid creating useless method:
public static void Foo(int x)
{
ParameterizedThreadStart p = new ParameterizedThreadStart(o => Bar((int)o));
Thread myThread = new Thread(p);
myThread.Start(x);
}
private static void Bar(int x)
{
// do work
}
Bar must take an object in parameter, not an int
private static void Bar(object x)
{
// do work
}

Delegate Array in C#

I am experimenting with calling delegate functions from a delegate array. I've been able to create the array of delegates, but how do I call the delegate?
public delegate void pd();
public static class MyClass
{
static void p1()
{
//...
}
static void p2 ()
{
//...
}
//...
static pd[] delegates = new pd[] {
new pd( MyClass.p1 ),
new pd( MyClass.p2)
/* ... */
};
}
public class MainClass
{
static void Main()
{
// Call pd[0]
// Call pd[1]
}
}
EDIT: The reason for the array is that I need to call the delegate functions by an index as needed. They are not run in response to an event. I see a critical (stupid) error in my code as I had tried to execute the delegate function using the pd[] type rather than the name of the array (delegates).
If they're all the same type, why not just combine them into a single multicast delegate?
static pd delegateInstance = new pd(MyClass.p1) + new pd(MyClass.p2) ...;
...
pd();
public class MainClass
{
static void Main()
{
pd[0]();
pd[1]();
}
}
In .Net, any delegate is in fact actually a "multicast" delegate (it inherits from this built-in base class), and therefore contains an internal linked list which can contain any number of target delegates.
You can access this list by calling the method GetInvocationList() on the delegate itself. This method returns an array of Delegates...
The only restriction is that all the delegates inside of a given delegate's linked list must have the same signature, (be of the same delegate type). If you need your collection to be able to contain delegates of disparate types, then you need to construct your own list or collection class.
But if this is ok, then you can "call" the delegates in a given delegate's invocation list like this:
public delegate void MessageArrivedHandler(MessageBase msg);
public class MyClass
{
public event MessageArrivedHandler MessageArrivedClientHandler;
public void CallEachDelegate(MessageBase msg)
{
if (MessageArrivedClientHandler == null)
return;
Delegate[] clientList = MessageArrivedClientHandler.GetInvocationList();
foreach (Delegate d in clientList)
{
if (d is MessageArrivedHandler)
(d as MessageArrivedHandler)(msg);
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pd[0]();
pd[1]();
}
public delegate void delegates();
static delegates[] pd = new delegates[]
{
new delegates(MyClass.p1),
new delegates(MyClass.p2)
};
public static class MyClass
{
public static void p1()
{
MessageBox.Show("1");
}
public static void p2()
{
MessageBox.Show("2");
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pd[0](1);
pd[1](2);
}
public delegate void delegates(int par);
static delegates[] pd = new delegates[]
{
new delegates(MyClass.p1),
new delegates(MyClass.p2)
};
public static class MyClass
{
public static void p1(int par)
{
MessageBox.Show(par.ToString());
}
public static void p2(int par)
{
MessageBox.Show(par.ToString());
}
}
}

Categories