1

Is it possible to pass a struct byref and readonly to a function? (just like T const& in C++)

struct A
{
    public int b;
}
void func1(ref A a)  // I want to make this `a` immutable
{

}

How to do that?

Update

My biggest concern is passing (1) immutable state (2) efficiently. Second concern is the mutating the state must be simple and easy as like mutable object.

eonil
  • 83,476
  • 81
  • 317
  • 516
  • Why would you want to pass it by ref then? – Adriaan Stander Jul 31 '13 at 04:41
  • @astander To save copy cost. – eonil Jul 31 '13 at 04:42
  • Why would that be your biggest concern? Are you facing performance/memory issues? – Adriaan Stander Jul 31 '13 at 04:43
  • My biggest concern is passing (1) immutable state (2) efficiently. Second concern is the mutating the state must be simple and easy as like mutable object. – eonil Jul 31 '13 at 04:56
  • I am making a soft realtime app, and this method will be called on all objects for each frame. So I think I need to make this very efficient as much as possible. – eonil Jul 31 '13 at 04:58
  • 1
    Why would your `struct` be mutable at all? – Guillaume Jul 31 '13 at 06:20
  • @Guillaume Can I make immutable struct??? – eonil Jul 31 '13 at 06:48
  • Your struct is already immutable, nobody can get to the "b" member. – Hans Passant Jul 31 '13 at 09:41
  • @HansPassant I fixed the bug. – eonil Jul 31 '13 at 11:08
  • Well, don't do that. Use a property instead so it can be immutable. – Hans Passant Jul 31 '13 at 11:47
  • If you can change this code, make the struct immutable. Also, this struct has the size of an `int`, using `ref` will not make any difference here in relation to "copy cost". – Lasse V. Karlsen Jul 31 '13 at 16:42
  • @Guillaume: *All* non-trivial struct types are mutable. Given two storage locations `s1` and `s2` of the same type, the first of which is writable, `s1=s2;` will mutate s1 by copying all its public and private instance fields from the corresponding ones in s2. If a struct-type instance is in a writable storage location, all of its instance fields will be writable. If it's in a non-writable storage location, none of its instance fields will be writable. No struct type has any ability to control these behaviors in any way. – supercat Jul 31 '13 at 19:52
  • @LasseV.Karlsen: If a struct is very small, copying it will be just as fast as passing a `ref`. Efficiency-minded code, however, will often use structs much larger than that. If one needs value semantics but will be more often making slight modifications to things than copying them, a struct of essentially any size may be vastly more efficient than would be an immutable class with the same fields. While small structs are good for unified values (e.g. `Decimal`), exposed-field structs are a perfect way means to bundle fixed sets of related but independent values (e.g. coordinates of a point). – supercat Jul 31 '13 at 20:09

3 Answers3

1

Currently I am doing this like this. A kind of boxing.

class
ImmutableBox<T> where T : struct, new()
{
    public readonly T state;
    ImmutableBox(T state)
    {
        this.state = state;
    }
}

struct
ExampleStateA
{
    public string someField;
}

void
func1(ImmutableBox<ExampleStateA> passImmutableStateWithBox)
{
}

By keeping the instance of ImmutableBox<T>, I can pass immutable object with pointer copy, and it's still easy to edit State because State is mutable. Also I gain a chance to optimize equality comparison to pointer comparison.

svick
  • 236,525
  • 50
  • 385
  • 514
eonil
  • 83,476
  • 81
  • 317
  • 516
  • That's a very good pattern to use. It's also useful to have a `class ExposedValueHolder {public T Value;}` [possibly with parameterized constructor, `ToString`, etc. but with the primary semantics revolving around the fact that it has no state but the exposed field]. Sometimes value semantics are useful, sometimes immutable semantics are useful, and sometimes mutable reference semantics are useful. I wish the Framework had a few types like these, so as to avoid the need for everyone to reinvent the wheel. – supercat Jul 31 '13 at 19:46
  • @supercat Personally, I think immutable state must be the primary choice of design, and mutable state must be used only for local optimization. (of course the scope of *local* can be vary...) This pattern really fits to my needs. – eonil Aug 01 '13 at 00:11
  • Classes should generally avoid exposing references to mutable objects they use to encapsulate state, but an `ExposedValueHolder` can be very useful if one wants to have something like a private `Dictionary` or `List` of structure values and be able to efficiently edit the things stored therein. If `List` had an `ActOnItem(index T, ActionRR proc, ref TX extraParam)` method (which would call a delegate with two `ref` parameters), things could be modified in-place without having to be wrapped, but no such method exists and... – supercat Aug 01 '13 at 01:06
  • ...there's no way to write a compatible `List` derivative to add one. – supercat Aug 01 '13 at 01:06
0

I dont think there is a way to make a ref parameter immutable. Instead, you should make your structs immutable in the first place to avoid side effects. Read more here:

Why are mutable structs evil

Community
  • 1
  • 1
Tsabo
  • 834
  • 4
  • 9
  • There is no such thing as an "immutable struct". If a `struct1` and `struct2` are the same type, and `struct1` is a writable storage location, the statement `struct1 = struct2` will mutate all fields of `struct1` by replacing their contents with the corresponding fields of `struct2`. Making a struct pretend to be immutable will make small changes far less efficient, but won't protect any instances from accidental modification. An "immutable" design is okay to represents a single unified value, but to represents a set of related but independent values an exposed-field struct is much better. – supercat Jul 31 '13 at 14:01
  • In any case, so-called "immutable" struct will do nothing to solve the poster's stated problem. – supercat Jul 31 '13 at 14:01
  • Hey, there a a link to a thread containing a post by Mark Gravel, who is quoting Eric Lippert on the subject. I'm sure that you know better then one of the top rated posters and a c# language designer tho, so why would you care what they have to say. – Tsabo Aug 01 '13 at 05:32
  • Eric Lippert himself has noted that "mutable structs" aren't. As for the guideline Mr. Lippert cites, I would posit that there are two major usage cases for structures: as replacements for immutable class objects, and as fixed collections of related but independent variables (e.g. the coordinates of a point). The guidelines are appropriate for structures that are designed to handle *the first usage case* (i.e. that are supposed to behave like classes), but ignore the existence of the second. If one wants a structure to behave like a class, the guidelines are fine, but... – supercat Aug 01 '13 at 15:22
  • ...piecewise-mutable value semantics are often useful in their own right precisely *because* they're different from class semantics; notice that many XNA data types which serve that purpose and were written after the guidelines don't follow them (because they represent collections of related but independent variables). It's also worth noting that there are some differences between the way C# views the universe and the way the .NET runtime views the universe. C# language architects like to view such differences as defects in the runtime; I view such differences as defects in the language. – supercat Aug 01 '13 at 15:32
  • In any case, all of the state encapsulated by a variable passed by `ref` will be mutable regardless of how its type is defined. Given signature `void Foo(ref T it)` the statement `it = default(T)` is guaranteed to be legal and will nuke any state which `it` happens to hold, regardless of its type. The only aspect of `it`'s type the runtime will even look at is the number of bytes the referenced variable occupies. – supercat Aug 01 '13 at 15:41
0

There is alas no concept of const ref in .NET. If the struct is small, you should pass it by value. If it's large, you must decide whether you want to trust the recipient of the struct not to modify it (in which case you can efficiently pass by ref) or put up with the inefficiency of passing by value.

Note that making structs pretend to be immutable will do nothing to help your problem, but will simply impair efficiency in cases where you want to modify part it. One should often avoid having struct methods which mutate the underlying struct since compilers try to fake "const ref" member invocation by silently converting pass-by-reference member invocation on read-only members to to pass-by-value, but there's no such problem with structs that expose fields.

supercat
  • 77,689
  • 9
  • 166
  • 211