tags:

views:

139

answers:

3

Hi,

I am having some troubles passing a reference to an object which is of generic type. I have found a way around by creating a 'Object' and passing a reference to that rather than the original - but it seems to smell a bit to me. Is there a better way here or do I have to live with it?

I understand the first error but the second eludes me.

public static T Foo<T>(ref T Bar)
{
    T Result;

    // Next line gives
    // cannot convert from 'ref T' to 'ref object'
    Result = (T)ModifyObject (ref Bar);

    // Next line gives
    // A ref or out argument must be an assignable variable
    Result = (T)ModifyObject (ref ((Object)Bar) );

    // Works
    Object Tmp = Bar;
    Result = (T)ModifyObject (ref Tmp) );

    return Result;

}

public static Object DoSomthing(ref Object Obj) {
    Object Result = Activator.CreateInstance (Obj.GetType ())
    //...
}

DoSomething is not generic as it uses recursion where the type of Obj can change. I was trying to stick away from using reflection to call a generic version of it, although on posting maybe it would be a better option?

+4  A: 

The type of a ref argument should match the type of the parameter. You cannot rely on implicit conversions here. Eric Lippert has a related blog post: Why do ref and out parameters not allow type variation?

Mehrdad Afshari
While the first error is explained by that. I don't understand the second, where I am explicitly casting it to an object (the same type of parameter as the function) before applying the ref operator.
Courtney de Lautour
@Courtney: A `ref` parameter expects a "variable" of the declared type, **not a value**. You can't pass 42 to a `ref int` parameter. Casts operate on values, not variables. You need to give the callee a **place** to hold an `object`. This place cannot be an `int` variable, for instance, because the callee might like to put a `string` into it.
Mehrdad Afshari
I Gotcha now:) - So that would mean that the solution I have is the best way to do it?
Courtney de Lautour
If you don't need `ref`, then don't use it. You might want to pass the type in the first place, instead of the actual object.
Mehrdad Afshari
A: 

For the second example, you will need to cast it to an object variable first:

object obj = (object)Bar;
Result = (T)ModifyObject (ref obj);

But after the method is executed, the only thing that is sure is that obj will be of type Object. That is what compiler is warning you about.

And the code does smell a bit. If you are returning a result of type T, then I don't see a reason to pass the parameter by reference. Second thing, you don't need to pass an instance of your type, by reference, in order to create it. This method would work just fine:

public static Object DoSomething(Type objType) {
    Object Result = Activator.CreateInstance(objType)
}

And finally, if you are using generics, then there should not be a reason to do all the casting. That is exactly why you are using a generic parameter, to make your class a template for different types.

Groo
+1  A: 

The second error message already contains an explanation:

A ref or out argument must be an assignable variable

That’s it: you create a new object by casting: (Object) bar may reference the same underlying object but it’s a different value nonetheless. Furthermore, it’s a temporary value because you never assigned it to a distinct variable name. Thus, passing it by reference is meaningless – any change to that temporary object would be lost. Thus, temporaries are strictly rvalues: you cannot assign to them, or pass them by reference.

This is also why your third code works: you’ve now bound the result of the conversion to a new name and this name can be used as an lvalue, i.e. it can be changed (assigned to, passed by reference).

Konrad Rudolph