views:

340

answers:

4

I'm tring to build a library for simplifing late binding calls in C#, and I'm getting trouble tring with reference parameteres. I have the following method to add a parameter used in a method call

    public IInvoker AddParameter(ref object value)
    {
        //List<object> _parameters = new List<object>();
        _parameters.Add(value);          

        //List<bool> _isRef = new List<bool>();
        _isRef.Add(true);

        return this;
    }

And that doesn't work with value types, because they get boxed as an object, thus they are not modified. E.g:

int param1 = 2;
object paramObj = param1;
//MulFiveRef method multiplies the integer passed as a reference parameter by 5:
//void MulFiveRef(ref int value) { value *= 5; }
fi.Method("MulFiveRef").AddParameter(ref paramObj);

That doesn't work. The late binding call is successful, and the inner List which holds the parameteres (_parameters ) does get modified, but not the value outside the call.

Does anyone knows a simple way to overcome this limitation? The AddParameter signature cannot be modified, as with late binding calls, you cannot know in advance the Type of the parameters (and either way you insert all the parameters for a call inside an object array prior to making the call)

Thanks in advance.

+1  A: 

Your method isn't changing value anyway - why are you passing it by reference? It may make sense, but it's not really clear to me. Note that the sample code you've provided wouldn't compile anyway, as ref arguments have to be exactly the same type as the parameter.

(Also, are you aware that C# 4.0 and .NET 4.0 will have built-in support for late-binding? Chances are that the language-integrated version will be easier to use than a library-only one. Are you sure it's worth spending time on the library at this point in time?)

EDIT: The code you've provided really won't compile. You don't get boxing for ref parameters, precisely because the argument and parameter types have to be exactly the same. Here's some sample code to prove it:

public class Test
{
    static void Main()
    {
        int i;
        Foo(ref i); // Won't compile - error CS1502/1503
    }

    static void Foo(ref object x)
    {
    }
}

If your current code is compiling, then it's not the code you presented in the question. Perhaps you have another overload for AddParameter which accepts ref int?

Jon Skeet
My apologies, you were right. The code i posted originaly does not compile.
Ricky AH
+1  A: 

If the value is changing inside the method, you will need to declare a temp (object) variable to pass (ref) to the method, and unbox it yourself afterwards:

    int i = 3;
    //...
    object obj = i;
    Foo(ref obj);
    i = (int)obj;

Note that this will not allow you to update the value after the event. Something like an event or callback might be an alternative way of passing changes back to the caller.

Note also that C# 4.0 has some tricks to help with this only in the context of COM calls (where ref object is so common [plus of course dynamic for late binding, as Jon notes]).

Marc Gravell
A: 

Jon, i'm aware of C#4.0 dynamic keyword, but C#4.0 isn't even released... and i'm using 2.0 at work. :(

Its a really simply library. In fact, i´ve already done it (search in google code por LateBindingHelper) but i'm reimplementing it with a fluent interface :)

Lastly, the code does compile as any value type is automaticly boxed into an object, and any reference type... well, is in essence an object ;)

Marc Gravell, the library is aimed to work with COM, so a simple use of reference parameters is important. The idea is do something like this:

        Invoker fi = new Invoker(Activator.CreateInstance<MyLateBindingTestType>());

        int param1 = 2;
        object paramObj = param1;

        fi.Method("MulFiveRef").AddParameter(ref paramObj).Invoke();

        param1 = (int)paramObj;

If all works right, param1 sould be 10 after calling .Invoke(), but i cannot change the value by reference once i've exited the AddParameter method.

Who whould have say that i'll be missing working with raw pointers :)

Ricky AH
The code you gave in the question *won't* compile. Please see my edited answer.
Jon Skeet
A: 

Ok, thanks to Jon Skeet corrections and Mark Gravell code, i've come up with this interface:

        //This will be created with a factory
        IOperationInvoker invoker = new OperationInvoker(Activator.CreateInstance<MyLateBindingTestType>());

        int param1 = 2;
        object paramObj = param1;

        invoker.AddParameter(ref paramObj).Invoke("MulFiveRef");

        param1 = (int)invoker.Parameters[0];

Is not exactly as I've imagined ( "I don't know, I can imagine quite a bit" ) ,but is way more simply and readable that my previous interface:

        IOperationInvoker invoker = new OperationInvoker(Activator.CreateInstance<MyLateBindingTestType>());
        int refValue = 10;
        object[] args = Args.Build(refValue);

        invoker.Call("MulFiveRef", Args.ByRefIndexs(0), args);

        refValue = (int)args[0];

Thank you very much for your help people :)

Ricky AH