Using Action as an argument in C# (mimicking a

2020-05-28 10:42发布

问题:

I need to write a delegate function that can 'wrap' some while/try/catch code around a basic UDP call to verify the link. I made it work for Func for a function that has no arguments, but I can't make it work for Action, which has an argument (but no return). I can't seem to pass in the argument in a logical way without the compiler complaining.

Am I going about this all wrong? I'm new to C# and I'm essentially trying to mimick the idea of a function pointer. Should I not be overloading this function? I know you can't overload delegates (I assume that's why Func and Action exist).

This works:

protected TResult udpCommand<TResult>(Func<TResult> command)
        {
            TResult retValue = default(TResult);
            while (!linkDownFail)
            {
                try
                {
                    retValue = command();
                    break;
                }
                catch
                {
                    LinkStateCallBack(ip, getLinkStatus());
                    if (linkDownFail) throw new LinkDownException();
                    Thread.Sleep(100);
                }
            }
            return retValue;
        }

But this does not:

protected void udpCommand<T>(Action<T> command(T value))
        {
            while(!linkDownFail)
            {
                try
                {
                    command(value);
                    break;
                }
                catch
                {
                    LinkStateCallBack(ip, getLinkStatus());
                    if (linkDownFail) throw new LinkDownException();
                    Thread.Sleep(100);
                }
            }
            return;
        }

Calling convention (for one that works):

udpCommand<uint>(someUdpCommand);

回答1:

If you want this to be generic enough to handle any number of arguments, try using the non-genernic Action delegate:

protected void udpCommand(Action command)
{
    while(!linkDownFail)
    {
        try
        {
            command();
            break;
        }
        catch
        {
            LinkStateCallBack(ip, getLinkStatus());
            if (linkDownFail) throw new LinkDownException();
            Thread.Sleep(100);
        }
    }
    return;
}

In C# 3.0, you can call it like this:

udpCommand(() => noParameterMethod());
udpCommand(() => singleParameterMethod(value));
udpCommand(() => manyParameterMethod(value, value2, value3, value4));

In C# 2.0 it's a little uglier:

udpCommand(delegate { noParameterMethod(); });
udpCommand(delegate { singleParameterMethod(value); });
udpCommand(delegate { manyParameterMethod(value, value2, value3, value4); });

This gives you deferred execution without locking you into a particular method signature.

EDIT

I just notice I kinda stole Marc Gravell's comment... apologies Marc. To answer how you might reduce your duplication, you can have the Action method call the Func<T> method, like this:

protected void udpCommand(Action command)
{
    udpCommand(() => { command(); return 0; });
}

I believe (and I may be wrong) that returning 0 is no more costly than (implicitly) returning void, but I may be way off here. Even it it does have a cost, it would only put a tiny itty bitty snoodge extra on the stack. In most cases, the additional cost won't ever cause you any grief.



回答2:

Do you mean:

    protected void udpCommand<T>(Action<T> command, T value) {...}

With calling:

udpCommand(someUdpCommand, arg);

Note that this may work better on C# 3.0, which has stronger generic type inference than C# 2.0.



回答3:

I think you just need to take out the (T value) after 'command'.



回答4:

Are you trying to do this ...

protected void udpCommand<T>(Action<T> command, T value)
{
   while(!linkDownFail)
   {
    try                
    {
      command(value);
      // etc.
    }
  }
}

Then it would work like this ...

public void ActionWithInt( int param )
{
   // some command
}

Action<int> fp = ActionWithInt;

udpCommand<int>( fp, 10 );  // or whatever.


标签: c# delegates