Using methods as a default parameter - c#

I'm looking to create a Button class in my custom XNA GUI that accepts methods as an argument, similar to how, in Python's tkinter you can just set the function to be called with Button.config(command = a_method) .
I've read about using delegates as parameters here, here and here, but I don't seem to be any closer to getting it working. I don't fully understand how delegates work, but I've tried several different things unsuccessfully, like using Func<int>? command = null in order to test later to see if command is null then I'd call the preset default, but then I get a Func cannot be nullable type or something similar.
Ideally the code'd be something like:
class Button
{
//Don't know what to put instead of Func
Func command;
// accepts an argument that will be stored for an OnClick event
public Button(Action command = DefaultMethod)
{
if (command != DefaultMethod)
{
this.command = command;
}
}
}
But it seems like everything I've tried is not working out.

Default parameters must be a compile time constant. In C#, Delegates can't be constants. You can achieve a similar result by providing your own default in the implementation. (just using Winforms here)
private void button1_Click(object sender, EventArgs e)
{
Button(new Action(Print));
Button();
}
public void Button(Action command = null)
{
if (command == null)
{
command = DefaultMethod;
}
command.Invoke();
}
private void DefaultMethod()
{
MessageBox.Show("default");
}
private void Print()
{
MessageBox.Show("printed");
}

The error you got about Func<T> not being nullable it right - it is a reference type and only value types can be nullable.
To default a Func<T> parameter to null, you can simply write:
Func<int> command = null

If you are interested in a default value, would something like this work?
class Button
{
//Don't know what to put instead of Func
private readonly Func defaultMethod = ""?
Func command;
// accepts an argument that will be stored for an OnClick event
public Button(Action command)
{
if (command != defaultMethod)
{
this.command = command;
}
}
}

Related

C# How can i detect EvenHandler subscription in Roslyn

I'm writing a custom analyzer rule using Roslyn.
I want to find a method which is a handler for some event (via subscription).
Like this:
public class Counter
{
public event EventHandler ThresholdReached;
}
public class TestEvent
{
public TestEvent()
{
Counter с = new Counter();
с.ThresholdReached += OnThresholdReached;
}
private void OnThresholdReached(object sender, EventArgs e)
{
}
}
In my realization it looks:
private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
{
MethodDeclarationSyntax methodDeclaration = (MethodDeclarationSyntax)context.Node;
if (methodDeclaration.Identifier.IsMissing)
{
return;
}
IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration);
}
I don't know how to detect that OnThresholdReached is subscription of Event ThresholdReached. If someone knows how to do it, please help=)
In an analyzer, you cannot know only from looking at a MethodDeclarationSyntax, whether that method is converted to a delegate or not. Because of that, you can not know (only from looking at a MethodDeclarationSyntax) whether that delegate is passed to the add accessor of an event or not.
First of all, remember that a Roslyn analyzer can only see usages in the current assembly (project). If your method is converted to a delegate in another assembly, there is no way for the analyzer to see this.
Secondly, remember that
с.ThresholdReached += OnThresholdReached;
may be expressed as
EventHandler handler = OnThresholdReached;
с.ThresholdReached += handler;
If you only want to detect the first case, you can look at AssignmentExpressionSyntax instances of kind SyntaxKind.AddAssignmentExpression, and analyze those.
If you want to detect all cases where a method group is converted to a delegate, you need to look at all instances of type SimpleNameSyntax and analyze those as follows:
void Analyze(SyntaxNodeAnalysisContext context)
{
var node = context.Node as SimpleNameSyntax;
// we're only interested in delegates
var type = context.SemanticModel.GetTypeInfo(node, context.CancellationToken).ConvertedType;
if (type == null || type.TypeKind != TypeKind.Delegate)
{
return;
}
// we're only interested in methods from the current assembly
var symbol = context.SemanticModel.GetSymbolInfo(node, context.CancellationToken).Symbol;
if (symbol == null ||
symbol.Kind != SymbolKind.Method ||
!symbol.ContainingAssembly.Equals(context.SemanticModel.Compilation.Assembly))
{
return;
}
// now you know symbol is a method in the same assembly, that is converted to a delegate
}
To find the source code for that method, see https://stackoverflow.com/a/45362532/1403794.

Why Generic method for delegate events not working with error message Method name expected

Work on C#.
My delegate and events are bellow
#region delegate event
#region
public delegate void NotifynextDeviceReceivedDelegate(CustomEventArgs customEventArgs);
public event NotifynextDeviceReceivedDelegate NotifynextDeviceReceivedEvent;
#endregion
#region
public delegate void NotifynextDeviceedDelegate(CustomEventArgs customEventArgs);
public event NotifynextDeviceedDelegate NotifynextDeviceedEvent;
#endregion
#region
public delegate void NotifynextReceiveddDelegate(CustomEventArgs customEventArgs);
public event NotifynextReceiveddDelegate NotifynextReceiveddEvent;
#endregion
#endregion
To invoke i used bellow syntax it's work perfectly
if (NotifynextDeviceReceivedEvent != null)
{
CustomEventArgs customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
//Raise Event. All the listeners of this event will get a call.
NotifynextDeviceReceivedEvent(customEventArgs);
}
Need to write same above syntax for all of the event delegate.So,i decided to write generic event to invoke them like bellow:
InvokeEvents<NotifynextDeviceReceivedDelegate>(receivedMessage,receivedTopic,NotifynextDeviceReceivedEvent)
public static InvokeEvents<T>(string receivedMessage,string receivedTopic, T notifynextDeviceReceivedEvent)
{
if (notifynextDeviceReceivedEvent != null)
{
CustomEventArgs customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
notifynextDeviceReceivedEvent(customEventArgs);//here is the problem show me error message
}
}
In InvokeEvents method why notifynextDeviceReceivedEvent show me error Method name expected
You could write your code as this:
private static void InvokeEvents<T>(string receivedMessage, string receivedTopic, T eventDelegate)
{
if (eventDelegate != null)
{
var customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
((Delegate)(object)eventDelegate).DynamicInvoke(customEventArgs);
}
}
This works. I tested it.
You do require the double cast to make the compiler happy.
There's also no point in making the accessor public as you can't pass a event delegate to it from anywhere outside of the class.
So, since you can't all this code from outside your class, you then must write code within your class to call InvokeEvents, like this:
public void OnNotifynextDeviceedEvent()
{
InvokeEvents("", "", this.NotifynextDeviceedEvent);
}
Which really then means you could have to repeat code in any case.
Now, with C# 6's syntax then you can write these methods as this:
public void OnNotifynextDeviceedEvent()
=> this.NotifynextDeviceedEvent?.Invoke(new CustomEventArgs("", ""));
So you're really not saving much code - none in fact - and you're creating a weakly-typed method. You really should stick with the basic approach.

Passing an Action with Different Parameters

I have a class in C# named Button and I want the Button have a functionality that can be passed through its constructor and whenever the Button was pressed the Action executes.
Button(Rect rect, string text, Action Func);
I have used Action and It worked perfectly until I found out that I can't pass a void Action with arguments.
For example:
void DoSomething(string str);
How can I be able to pass any void Action with any arguments?
The button doesn't have to care about the argument, but it still needs to be passed a delegate with no arguments and returning void. This is easily done:
new Button(rect, text, () => YourMethod(whateverArgument))
Depending on what you're trying to do, whateverArgument can be a local, a constant or a field. Just think about when it's supposed to read the value to pass to the inner method.
Sure you can pass arguments, just use the following:
Button b = new Button(.., .., () => DoSomething("YourString");
I advice you to use simplefied command pattern:
Create base class or interface Command
interface ICommand
{
void Execute();
}
//Create secific command and pass parameters in constructor:
class Command : ICommand
{
public Command(string str)
{
//do smth
}
void Execute()
{
//do smth
}
}
Button(Rect rect, string text, ICommand cmd)
{
cmd.Execute();
}

Convert delegate to System.Action

Introduction
In my WP8 C#/XAML project I'm using events to notify my view that some async process is done.
I have two types of delegates.
I'm creating events from those delegates and there are several of them notifying my view that some operations are completed or started (in order to show progressbar, navigate to another page, disable some controls et cetera).
In order to raise these events I want to create a private "notification method", which would raise those delegates.
The method i have in mind is in Code Sample below as fireUpEvent method.
Code Sample
ReturnState enum
public enum ReturnState : int
{
state1 = 0,
... //for the purpose of the presentation
state6 = 15
...
}
Definitions of events & methods
public delegate void LoadingStartedEventHandler();
public delegate void LoadingFinishedEventHandler(ReturnState state);
public event LoadingStartedEventHandler LoadingStarted;
public event LoadingFinishedEventHandler LoadingFinished;
private void fireUpEvent(Action<ReturnState> action, Returnstate parameter)
{
if(action != null)
{
action(parameter);
}
}
private void fireUpEvent(Action action)
{
if(action != null)
{
action();
}
}
Usage
fireUpEvent(LoadingFinished, ReturnState.state1);
Description
The problem is, that when I try to compile I get an error saying:
Argument1: Cannot convert from 'XXXX.YYYY.SomeClass.LoadingFinishedEventHandler' to 'System.Action<XXXX.YYYY.Returnstate>'
I've tried googling, but haven't found any usefull stuff.
Why isn't it convertible?
I'd like to Action<ReturnState> and Action in those methods instead of specific delegates, is it possible?
Should I use any other "type" like Action instead?
The only two I know from this "grooup" are Func & Action, are there others?
To answer this line:
Why isn't it convertible?
They're different delegate types, and there's no reference conversion between different delegate types (other than in generic delegate types using generic variance). You can create an Action from a LoadingStartedEventHandler like this:
LoadingStartedEventHandler handler = ...; // Whatever
Action action = new Action(handler);
... and you could do the same the other way round, and with your other delegate type and Action<T>. But there's really no need to do that.
I'd like to Action<ReturnState> and Action in those methods instead of specific delegates, is it possible?
Yes - just don't declare the events using those delegates, and indeed don't declare the delegates at all!
You can change this:
public delegate void LoadingStartedEventHandler();
public delegate void LoadingFinishedEventHandler(ReturnState state);
public event LoadingStartedEventHandler LoadingStarted;
public event LoadingFinishedEventHandler LoadingFinished;
To this:
public event Action LoadingStarted;
public event Action<ReturnState> LoadingFinished;
Note that this violates the .NET conventions on events, mind you. By convention, events are declared with a delegate where the first parameter is the "sender", of type object, and the second parameter is of a type derived from EventArgs (or EventArgs itself). Whether or not that's important to you is for you to decide. If you decide to follow the convention, you'd basically want to create a type deriving from EventArgs holding a ReturnState, and then replace the events with:
public event EventHandler LoadingStarted;
public event EventHandler<ReturnStateEventArgs> LoadingFinished;
And change your helper methods to:
private void RaiseEvent(EventHandler<TEventArgs> handler,
TEventArgs parameter)
{
if(handler != null)
{
handler(this, parameter);
}
}
private void RaiseEvent(EventHandler handler)
{
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
(I've modified the method names to follow .NET conventions and use the .NET terminology for events, which are "raised" rather than "fired up".)
Mr. Skeet always make good answers. Here I'm providing another solution of what you stated in the question.
For these two:
Why isn't it convertible?
I'd like to Action and Action in those methods instead of specific delegates, is it possible?
Mr. Skeet has already answered.
For these two:
Should I use any other "type" like Action instead?
The only two I know from this "grooup" are Func & Action, are there others?
Your fireUpEvent method probably should not accept an arbritary type of delegate as a brittle design. But it is possible to do.
The ultimate base type of all delegate type in C# is Delegate; a limitation of Delegate is that it cannot be a where constraint in generic types and methods.
To answer the question(regardless of the concerning of design), you can say:
private void fireUpEvent(
Delegate loadingEvent, ReturnState? parameter=null) {
if(null!=loadingEvent) {
foreach(var d in loadingEvent.GetInvocationList()) {
var args=null!=parameter?new object[] { parameter }:null;
d.Method.Invoke(d.Target, args);
}
}
}
instead of the original two methods in your question, where the Target is the object which owns the method, would be treated as this, null if it's a static method. args is the argument list passing to the method.
This is just a way that you can achieve it, and I'm pretty sure Mr. Skeet's answer is absolutely better.

c#, .net, delegate, asynchronous callback. What am I doing wrong here?

Heres a little class for a button in a menu in a game.
I'd like to be able to pass a delegate method _triggerMethod when I'm instantiating each button. Then that delegate method will get called when that button instances trigger method is called.
I'm trying out delegates in C# for the first time here. And as far as I'm interpreting the documentation here what I'm doing should work but visual studio is giving me a compile error in the Trigger method.
According to msdn article the code calling the delegate doesnt need to know about the original methods paramaters and such. What am I doing wrong?
Also in the msdn article they are only typing "Del" and that does not work for me. I must type "Delegate" which is odd.
class MenuItem
{
private Rectangle clickArea;
private string displayText;
private Vector2 _position;
private Delegate _triggerMethod;
public MenuItem(Vector2 pos,string txt,Delegate trig)
{
displayText = txt;
_position = pos;
_triggerMethod = trig;
}
public void Draw(SpriteBatch sb)
{
}
public void Select()
{
}
public void DeSelect()
{
}
public void IsMouseOnMe()
{
}
public void Trigger()
{
_triggerMethod();
}
}
You haven't created any delegate definition.
Example (from the MSDN-page you linked):
public delegate void Del(string message);
Then, you need to use that as your Type:
** snip **
private Del _triggerMethod;
public MenuItem(Vector2 pos,string txt,Del trig)
{
displayText = txt;
_position = pos;
_triggerMethod = trig;
}
** snip **
public void Trigger()
{
_triggerMethod("some message");
}
You can pass references of the delegate around without actually knowing what arguments it expects (since it's just a normal reference), but when you want to invoke it, you do need to give it the correct parameters.
You need to define the Del type if you want to use it:
public delegate void Del(string message);
public class MenuItem
{
private Del _triggerMethod;
public void Trigger()
{
_triggerMethod("Message");
}
}
Note that you can use the built-in Action<string> delegate type instead of defining your own:
Action<string> _triggerMethod;
_triggerMethod("Message");
If you just use the Delegate type, you can invoke it using DynamicInvoke:
public void Trigger()
{
_triggerMethod.DynamicInvoke();
}

Categories

Resources