Assigning locals to references to readonly fields (C# equivalent of "const &")

109 Views Asked by At

I've got a struct stored in a readonly field, nested in a few levels of objects (all in code that is outside of my control), that I am accessing in a function. For example:

struct Surprise { public readonly int a, b, c; }
struct Gizmo { public readonly Surprise surprise; }
struct Bobble { public readonly Gizmo gizmo; }
struct Thing { public readonly Bobble bobble; }
class Gift { public readonly Thing thing; }

void Function (Gift gift) {
    int contrived = gift.thing.bobble.gizmo.surprise.a +
                    gift.thing.bobble.gizmo.surprise.b + 
                    gift.thing.bobble.gizmo.surprise.c;
}

Purely to avoid having to type out gift.thing.bobble.gizmo.surprise every time I use it, I'd like to assign that to a local variable with a shorter name. However, I also don't need to copy the struct, so I'd like to avoid that:

void Function (Gift gift) {
    
    { // (A) this is what i'm trying to not type out:
        int contrived = gift.thing.bobble.gizmo.surprise.a + 
                        gift.thing.bobble.gizmo.surprise.b +
                        gift.thing.bobble.gizmo.surprise.c;
    }
    
    { // (B) i can do this, but i don't *need* to copy, so i'd like to avoid:
        Surprise s = gift.thing.bobble.gizmo.surprise;
        int contrived = s.a * s.b + s.c;
    }
    
    { // (C) what i *want* to do is something like this:
        ref Surprise s = ref gift.thing.bobble.gizmo.surprise;
        int contrived = s.a * s.b + s.c;
    }
    
}

However, it seems that variation (C) there is not allowed, and so doesn't compile:

CS0192 A readonly field cannot be used as a ref or out value
  (except in a constructor)

My question is: Is there a way to create a local variable that refers to a readonly value-typed field and doesn't copy it (or some other approach to save myself some typing)?

I'm fairly new to C#, coming from C++. If you're familiar with C++ I'm essentially looking for the equivalent of:

const Surprise &s = gift->thing.bobble.gizmo.surprise;

Also: Would it even make a difference, or is the compiler smart enough to not create a copy in (B) above?


By the way, I did, after much struggle, come up with this nightmare:

delegate void Hack (in Surprise s);

void Function (Gift gift) {
    ((Hack)((in Surprise s) => {
        int contrived = s.a + s.b + s.c;
        ...;
    }))(gift.thing.bobble.gizmo.surprise);
}

I'm not interested in that as an option, though, for hopefully obvious reasons. Also I'm not actually sure if that even avoids a copy.

2

There are 2 best solutions below

2
On

Create a readonly property to access the value you want.

public struct Surprise
{
    get
    {
        return gift.thing.bobble.gizmo.surprise;
    }
}
3
On

How about an extension method.

public static class GiftExtension
{
    public static int Sum(this Gift gift)
    {
        int contrived = gift.thing.bobble.gizmo.surprise.a +
                            gift.thing.bobble.gizmo.surprise.b +
                            gift.thing.bobble.gizmo.surprise.c;
        return contrived;
    }
    public static int Sum(this Surprise surprise)
    {
        int contrived = surprise.a +
                            surprise.b +
                            surprise.c;
        return contrived;
    }
}

then in your method, you can have

void Function(Gift gift)
    {
        {
            //(A)  was trying to use a pointer to access the memory location, couldn't get it to work!
            //unsafe
            //{
            //    Surprise* ps = &(gift.thing.bobble.gizmo.surprise);
            //}
        }
        //(B)
        {
            int contrived = gift.Sum();
        }
        {
            int contrived = gift.thing.bobble.gizmo.surprise.Sum();
        }
    }