views:

403

answers:

3

I have a class that is mostly a wrapper for a big array and some associated housekeeping. I have a function that takes a ref parameter. When I pass an instance of the class into the function, I want the array to get sent.

I considered explicit casts. Let's say I have some function that has a byte[] ref parameter.

    public void SomeFunction(ref byte[] someBytes);

And that I have some class with an overloaded explicit cast.

    class SomeClass
    {
        byte[] someBytes;
        public static explicit operator byte[](SomeClass someInstance)
        {
            return someInstance.someBytes;
        }
    }

Now I want to call the function with the class as a parameter

    SomeClass someInstance = new SomeClass();
    SomeFunction(ref (byte[]) someInstance);

The compiler complains "a ref or out argument must be an assignable variable". I'm not sure if I'm just failing to massage the compiler properly or if you really just can't do that.

I considered a Property or function return value, but you can't pass those by ref (and after educating myself I see why...)

I'd prefer not to make the array a public field, but that does satisfy the compiler. I suppose I could just create a local variable to reference the array with, but that's an extra line of code before and after each function call...

EDIT: it might be worth noting that SomeFunction was written by a third party and I don't have access to change it. Worse, I don't think their parameter actually needs to be ref...

+5  A: 

A cast is not an assignable variable; you're passing a return value from your explicit cast operator.

You can create a variable holding the properly cast value before passing it as a ref:

SomeClass someInstance = new SomeClass();
byte[] someBytes = (byte[])someInstance;
SomeFunction(ref someBytes);

Note that it is now the someBytes variable which may be reasssigned. You will have to take action to reassign someInstance.someBytes in some way after the call to SomeFunction if you want the internal value of someInstance to be reassigned.

Håvard S
So it turns out that the practical solution would either be a public field or a short method whose only purpose is to cover up the creation of some local variable to pass in. I did some more digging and generics might help, too.
ajs410
Right, a simple setter would suffice for updating someInstance. As an alternative, you could create an interface that SomeFunction takes as an argument and SomeInstance implements, and implement your logic through that interface.
Håvard S
A: 

You can't do that, you need to materialize the result of casting your object into a variable first.

Consider the following code, if what you ask for was allowed:

public void SomeFunction(ref byte[] someBytes)
{
    someBytes = new byte[] { 1, 2, 3 };
}

SomeClass someInstance = new SomeClass();
SomeFunction(ref (byte[]) someInstance);
// uh-oh, what is "someInstance" now?

Why is the argument marked "ref", what problem is it that you're trying to solve here?

Lasse V. Karlsen
The argument is marked ref likely because the original third-party developer was porting a C++ library to .NET. The problem I'm trying to solve is dancing around the ref without making someBytes a public field or creating the local variable. I chose the "explicit cast" because I knew the housekeeping fields would be lost. I was hoping someInstance would just pass the reference to someBytes along to SomeFunction. Not much different than using a public field, but forcing explicit casts would make it less prone to abuse.
ajs410
Well, "ref" and "out" parameters require a backing variable, there really is no way around that, so you just have to define the variable as you've already found out. You could of course make shim-methods in a class of your own to just do that translation for you, and call those methods instead, which would in turn do the cast, store in a variable, and call the actual methods, but that might not be a good solution either.
Lasse V. Karlsen
ajs410
+1  A: 

The scope of values that can be used as a ref parameter in C# is limited by what the CLR allows. It's specified in CLI spec at sections 12.4.1.5.2 and 12.1.6.1 and includes

  • Argument of the current method
  • Local variable
  • Member Field of an object
  • Static Field
  • Array Element

A cast does not fit any of these and hence cannot be used as ref value. You'll need to assign it to a value like a local before passing it by reference.

JaredPar
Thanks for the reference to the spec! I didn't get into C# for a while, so in a way this was more of an academic question as I fully explore the various features of the language...and there sure are a lot of them.
ajs410