2

I have two struct instances that have a reference-type variable. I want to swap those two variables, but my first guess on how to do this looks like it doesn't work. My code looks something like this:

struct Foo
{
    Bar m_bar;
}

void Main()
{
    Foo a = new Foo();
    Foo b = new Foo();

    //swapping the values of m_bar of a and b
    Bar temp = a.m_bar;
    a.m_bar = b.m_bar;
    b.m_bar = temp;
}

From what I could gather, it looks like since the variables are reference types, assigning b.m_bar to a.m_bar also assigns it to temp, messing up the swap.

So is my code wrong? If yes, what's the right way to swap two reference-type variables? If not, I guess my code messes up somewhere else.

Thanks!

4
  • 5
    "assigning b.m_bar to a.m_bar also assigns it to temp" - no, it doesn't; temp is an isolated snapshot of the reference as it was at the time you stored it (there is a way to do what you describe, but it requires special syntax - in particular "ref locals" - my point being: the code as shown should work fine) Commented Jun 15, 2020 at 11:54
  • 1
    Do you have the code that makes you think it isn't working? Note: mutable structs are almost always a bad idea (unless you really really know what you are doing, and even then: I'd probably advice against it, except in some very specific scenarios such as "ref structs") Commented Jun 15, 2020 at 11:57
  • Please share a minimal reproducible example where we can see the issue. Commented Jun 15, 2020 at 11:58
  • First of all you code will not compile due to access level of m_bar. Commented Jun 15, 2020 at 12:01

1 Answer 1

4

The code in the question works fine, as shown in the minimal repro below. The confusion here is almost certainly something to do with mutable value-types; structs with mutable fields are notorious for confusion. I strongly recommend treating Foo as immutable, explicitly making it a readonly struct if your compiler supports it - something like:

    readonly struct Foo
    {
        public Bar Bar { get; }
        public Foo(Bar bar) => Bar = bar;
        // not shown: override ToString, GetHashCode and Equals
    }

But: with something more like your original code:


static class P
{
    static void Main()
    {
        Foo a = new Foo { m_bar = new Bar("abc") };
        Foo b = new Foo { m_bar = new Bar("def") };

        System.Console.WriteLine("Before");
        System.Console.WriteLine($"a.m_bar: {a.m_bar}"); // abc
        System.Console.WriteLine($"b.m_bar: {b.m_bar}"); // def

        //swapping the values of m_bar of a and b
        Bar temp = a.m_bar;
        a.m_bar = b.m_bar;
        b.m_bar = temp;

        System.Console.WriteLine("After");
        System.Console.WriteLine($"a.m_bar: {a.m_bar}"); // def
        System.Console.WriteLine($"b.m_bar: {b.m_bar}"); // abc
    }

    struct Foo
    {   // note: public fields are usually a bad idea in any type
        // note: mutable fields on value-types are usually a bad idea
        public Bar m_bar;
    }

    class Bar
    {
        public string Name { get; }
        public override string ToString() => Name;
        public Bar(string name) => Name = name;
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.