Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting "params of ref" feature, any workarounds?

I wonder if there's any way something like this would be possible for value types...

public static class ExtensionMethods {
    public static void SetTo(this Boolean source, params Boolean[] bools) {
        for (int i = 0; i < bools.Length; i++) {
            bools[i] = source;
        }
    }
}

then this would be possible:

Boolean a = true, b, c = true, d = true, e;
b.SetTo(a, c, d, e);

Of course, this does not work because the bools are a value type so they are passed into the function as a value, not as a reference.

Other than wrapping the value types into reference types (by creating another class), is there any way to pass a variable into function by the reference (ref) while using params modifier?

like image 511
Kornelije Petak Avatar asked Nov 21 '09 16:11

Kornelije Petak


People also ask

What is the difference between a ref parameter and out parameter?

ref is used to state that the parameter passed may be modified by the method. in is used to state that the parameter passed cannot be modified by the method. out is used to state that the parameter passed must be modified by the method.

What is a ref parameter?

A reference parameter is a reference to a memory location of a variable. When you pass parameters by reference, unlike value parameters, a new storage location is not created for these parameters. The reference parameters represent the same memory location as the actual parameters that are supplied to the method.

What are Ref keywords?

The ref keyword indicates that a variable is a reference, or an alias for another object. It's used in five different contexts: In a method signature and in a method call, to pass an argument to a method by reference. For more information, see Passing an argument by reference.


4 Answers

This is not possible. To explain why, first read my essay on why it is that we optimize deallocation of local variables of value type by putting them on the stack:

https://web.archive.org/web/20100224071314/http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

Now that you understand that, it should be clear why you cannot store a "ref bool" in an array. If you could, then you could have an array which survives longer than the stack variable being referenced. We have two choices: either allow this, and produce programs which crash and die horribly if you get it wrong -- this is the choice made by the designers of C. Or, disallow it, and have a system which is less flexible but more safe. We chose the latter.

But let's think about this a little deeper. If what you want is to pass around "thing which allows me to set a variable", we have that. That's just a delegate:

static void DoStuff<T>(this T thing, params Action<T>[] actions)
{
    foreach(var action in actions) action(thing);
}
...
bool b = whatever;
b.DoStuff(x=>{q = x;}, x=>{r = x;} );

Make sense?

like image 62
Eric Lippert Avatar answered Nov 22 '22 05:11

Eric Lippert


There isn't really a way. You could do something like this:

public static void Main(string[] args)
{
    BooleanWrapper a = true, b = true, c = true, d = true, e = new BooleanWrapper();
    b.SetTo(a, c, d, e);
}

public static void SetTo(this BooleanWrapper sourceWrapper, params BooleanWrapper[] wrappers)
{
    foreach (var w in wrappers)
        w.Value = sourceWrapper.Value;
}

public class BooleanWrapper
{
    public BooleanWrapper() { }

    public BooleanWrapper(Boolean value)
    {
        Value = value;
    }

    public Boolean Value { get; set; }

    public static implicit operator BooleanWrapper(Boolean value)
    {
        return new BooleanWrapper(value);
    }
}

But then again how is that any better than just doing this:

public static void Main(string[] args)
{
    Boolean[] bools = new Boolean[5];
    bools.SetTo(bools[1]); // Note I changed the order of arguments. I think this makes more sense.
}

public static void SetTo(this Boolean[] bools, Boolean value)
{
    for(int i = 0; i < bools.Length; i++)
        bools[i] = value;
}

After all, an array is a sequence of variables. If you need something that behaves like a sequence of variables, use an array.

like image 20
Joren Avatar answered Nov 22 '22 06:11

Joren


Unfortunately the community of Java, and now .NET, developers decided that less flexibility in the name of "safety" is the preferred solution, and to achieve the same result with less lines of code you have to opt for extraordinary complexity (all those class structures, delegates, etc.).

In Delphi I could simply do something like this:

var
  a: integer; f: double; n: integer;
sscanf(fmtstr, valuestr, [@a, @f, @n]);

//<-- "sscanf" is a function I wrote myself that takes an open array of pointers.

In C# You would have to do:

int a; double f; int n;
object [] o = new object[];
sscanf(fmtstr, valuestr, ref o);
a = o[0];
f = o[1];
n = o[2];

That's 5 lines of code to do what I could do in 1 line of Delphi code. I think there is a formula somewhere that the likelihood of bugs in code increases geometrically with the number of lines of code; so if you have 20 lines of code you're code is 4 times more likely to have bugs than if you have 10.

Of course, you can decrease your # lines of code by using the delegate with all those weird angle brackets and strange syntax, but I would think that's also a haven for bugs.

like image 34
BobT Avatar answered Nov 22 '22 05:11

BobT


Here is some interesting solution:

public delegate RecursionRefFunc<T> RecursionRefFunc<T>(ref T arg);

public static RecursionRefFunc<T> Boo<T>(ref T input)
{
    Console.WriteLine(input); // Work in here
    return Boo;
}

public static void Main(string[] args)
{
    int x1 = 1, x2 = 2, x3 = 3, x4 = 4, x5 = 5;
    Boo(ref x1)(ref x2)(ref x3)(ref x4)(ref x5);
}

// Output: //
// 1
// 2
// 3
// 4
// 5

Delegate can declare in recursion.

Return a function outside and call again.

And you will be killed by the code reviewer.


Advertisement OW<: CWKSC/MyLib_Csharp

like image 45
CWKSC Avatar answered Nov 22 '22 05:11

CWKSC