Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 278
mishan Avatar asked Nov 08 '13 06:11

mishan


2 Answers

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".)

like image 71
Jon Skeet Avatar answered Sep 22 '22 14:09

Jon Skeet


Mr. Skeet always make good answers. Here I'm providing another solution of what you stated in the question.

For these two:

  1. Why isn't it convertible?
  2. 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:

  1. Should I use any other "type" like Action instead?
  2. 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.

like image 31
Ken Kin Avatar answered Sep 25 '22 14:09

Ken Kin