tags:

views:

220

answers:

4

I have a function that wraps a call to one of my socket types. If there is an error, I want to be able to print a warning and retry. In the warning, I want to have the method name. However, it was declared as a lambda. Is this even possible?

How I call the function (assume in function called myMain):

SafeSocketCommand(() => this.mySocket.ReadCurrentBuffer());

Basic wrapping function:

protected TResult SafeSocketCommand<TResult>(Func<TResult> socketCommand)
{
    TResult retValue = default(TResult);
    try
    {
        retValue = socketCommand();
    }
    catch (PacketLost)
    {
        ReportToLogs("Timeout on command '" + socketCommand.Method.Name);
    }
    return retValue;
}

But socketCommand.Method.Name gives me the calling method (from the Stack Trace?) '< myMain >b__3' and I want the actual function being invoked by socketCommand (mySocket.ReadCurrentBuffer). Is it possible to get this information anywhere, or is it lost due to declaring in a lambda?

EDIT:

I should have mentioned that I use this particular calling convention so that I can use socket based commands of various signatures.

int i = SafeSocketCommand(() => this.mySocket.FunctionReturnsInt())
bool b = SafeSocketCommand(() => this.mySocket.FunctionReturnsBool(string s))
object o = SafeSocketCommand(() => this.mySocket.Complicated(string s, int i, bool b))

It also handles no return type signatures by overloading:

protected void SafeSocketCommand(Action socketCommand)
{
    SafeSocketCommand(() => { socketCommand(); return 0; });
}
+2  A: 

No, because lambda's don't have names; they're anonymous functions. You could get the method name from the last stackframe, though:

new StackFrame(1).GetMethod().Name;
Joel Coehoorn
Could an exception be in the StackFrame at that point, though? What if it was caught, repackaged and thrown a second time?
cgyDeveloper
You're thinking stack- **trace**. This code just checks an individual frame.
Joel Coehoorn
A nice way to get the method name without < >b__3, but I'll probably have to try using Expression and parsing the tree if I want the function name.
cgyDeveloper
+2  A: 

Func<TResult> is just a delegate. Rather than use a lambda, create a method that matches the signature of Func<TResult> and call that. That way, you'll have whatever name you want.

SafeSocketCommand(MyNewMethod);

...

public TResult MyNewMethod()
{
    return this.mySocket.ReadCurrentBuffer();
}
Aaron Daniels
+5  A: 

If you modify your SafeSocketCommand to accept an Expression<Func<TResult>> then you'll get access to an expression tree that represents the body of the lambda, from which you can access the ReadCurrentBuffer call directly.

However, if you do this, you're no longer dealing with a regular anonymous method; to actually call it you'll need to compile the expression tree to code. You may also need to be flexible as to what your code expects to appear inside the lambda's body.

Tim Robinson
Interesting, I wasn't aware of Expression. Since this is no longer a regular anonymous method, will it cause any of my various calling conventions to fail (where I can call regardless of function signature)?
cgyDeveloper
LINQ itself is built on these expression trees, so I don't see any problems based on the calling conventions in your question.
Tim Robinson
I'll have to give a shot, then. Since the format of the lambda is always one of two styles (depending on whether ;return 0; is added or not) it should be easy enough to parse the tree for my function name.
cgyDeveloper
A: 

In this case, you can simply this call instead. It'll be faster and smaller generated code too.

SafeSocketCommand(mySocket.ReadCurrentBuffer);

In general, the StackTrace of the Exception object contains the full information you are looking for, much more accurately than printing the method name, or you can use the TargetSite property for the name of the method that threw the exception.

280Z28