ParameterCollection usage of named output arguments

Mar 25, 2009 at 10:58 PM
Hi everyone,

Quick question: when I wrote an interception call handler for tracing (code fragment below) I stumbled upon the following. I direct the IMethodInvocation and IMethodReturn variables to a TraceCall method that dumps the information to a debug window. I also want this to work for methods like this:

string Foo(bool x, ref string y, out int z);

For my implementation I loop through the collection of input parameters and retrieve their names. Using these names and the input argument info I check to see whether the argument is ref or out. If so, I look up the outputs of the IMethodReturn based on the parameter name of the input parameter (say y or z in the case above). To my surprise this approach gives me the wrong output argument. In particular, asking for result.Outputs["z"] gives the output for y instead.
Why does this happen?

My best guess is this:
It's a bug. With Reflector (or the unity source code) you can see that internally two collections are used in ParameterCollection (i.e. type of result.Outputs): the list of all arguments passed to the method and a list of ArgumentInfos that is built by filtering all arguments of the method by applying a predicate during construction of ParameterCollection. For the Foo method, the list of arguments is 3, whereas the outputs arguments is just 2 (1 for ref and 1 for out). When the indexer is used on the ParameterCollection, it does do a neat lookup of the parameter name in the list of parameters (again, of size 2), but uses the index in the list to look up the value of the argument in the other list (3 large). Therefore it is one off. I think the .Index property of the ArgumentInfo object should have been used, as this contains the correct index in the unfiltered list of parameters. Then the index would be usable in the arguments array as well.
Am I on the right track here?

Relevant code fragment of call handler using ParameterCollection

public class TraceCallHandler : ICallHandler
{
  public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  {
   IMethodReturn result = getNext().Invoke(input, getNext);
   // Do interception tracing
   TraceCall(input, result);
   return result;
  }

  private void TraceCall(IMethodInvocation input, IMethodReturn result)
  {
   StringBuilder builder = new StringBuilder();
   builder.AppendFormat("{0}::{1}(", input.MethodBase.ReflectedType.Name, input.MethodBase.Name);

   List<string> arguments = new List<string>();
   bool first = true;
   for (int index = 0; index < input.Arguments.Count; index++)
   {
    if (!first) builder.Append(", ");

    string paramName = input.Arguments.ParameterName(index);
    ParameterInfo info = input.Arguments.GetParameterInfo(paramName);
    builder.AppendFormat("{0} = {1}", info, input.Arguments[paramName]);

    if (info.ParameterType.IsByRef || info.IsOut)
    {
     // BUG?: 
     builder.AppendFormat("=> {0}", result.Outputs[paramName]);

Mar 26, 2009 at 9:36 PM
It's probably a bug. Could you please enter a work item so we can track it?

Thanks,

-Chris

Mar 31, 2009 at 8:29 AM
Hi Chris,

Just added a new issue: http://unity.codeplex.com/WorkItem/View.aspx?WorkItemId=4151
My suggested fix is in there as well.

Alex