Why are C# structs immutable?

丶灬走出姿态 提交于 2020-12-25 03:24:43

Compiler Error CS1612 Cannot modify the return value of 'expression' because it is not a variable

class Transform
    {
        public Point p { get; set; }

        public Point p2;
        public void ShowV()
        {
            Console.WriteLine(p.X + "..." + p.Y);
        }
    }


        [Test]
        public void TestChuck()
        {
            Transform t = new Transform();
            t.p.X = 1;
            t.p2.X = 2;
            t.ShowV();
            Console.Read();
        }

t.p = new Point();  这个可以,set可以工作。 但是get出来的是value

 

 

Why are C# structs immutable?

问题

I was just curious to know why structs, strings etc are immutable? What is the reason for making them immutable and rest of the objects as mutable. What are the things that are considered to make an object immutable?

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

 

 解答:

If this subject interests you, I have a number of articles about immutable programming at http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/

I was just curious to know why structs, strings etc are immutable?

Structs and classes are not immutable by default, though it is a best practice to make structs immutable. I like immutable classes too.

Strings are immutable.

What is the reason for making them immutable and rest of the objects as mutable.

Reasons to make all types immutable:

  • It is easier to reason about objects that do not change. If I have a queue with three items in it, I know it is not empty now, it was not empty five minutes ago, it will not be empty in the future. It's immutable! Once I know a fact about it, I can use that fact forever. Facts about immutable objects do not go stale.

  • A special case of the first point: immutable objects are much easier to make threadsafe. Most thread safety problems are due to writes on one thread and reads on another; immutable objects don't have writes.

  • Immutable objects can be taken apart and re-used. For example, if you have an immutable binary tree then you can use its left and right subtrees as subtrees of a different tree without worrying about it. In a mutable structure you typically end up making copies of data to re-use it because you don't want changes to one logical object affecting another. This can save lots of time and memory.

Reasons to make structs immutable

There are lots of reasons to make structs immutable. Here's just one.

Structs are copied by value, not by reference. It is easy to accidentally treat a struct as being copied by reference. For example:

void M()
{ S s = whatever; ... lots of code ... s.Mutate(); ... lots more code ... Console.WriteLine(s.Foo); ... }

Now you want to refactor some of that code into a helper method:

void Helper(S s) { ... lots of code ... s.Mutate(); ... lots more code ... }

WRONG! That should be (ref S s) -- if you don't do that then the mutation will happen on a copy of s. If you don't allow mutations in the first place then all these sorts of problems go away.

Reasons to make strings immutable

Remember my first point about facts about immutable structures staying facts?

Suppose string were mutable:

public static File OpenFile(string filename) { if (!HasPermission(filename)) throw new SecurityException(); return InternalOpenFile(filename); }

What if the hostile caller mutates filename after the security check and before the file is opened? The code just opened a file that they might not have permission to!

Again, mutable data is hard to reason about. You want the fact "this caller is authorized to see the file described by this string" to be true forever, not until a mutation happens. With mutable strings, to write secure code we'd constantly have to be making copies of data that we know do not change.

What are the things that are considered to make an object immutable?

Does the type logically represent something that is an "eternal" value? The number 12 is the number 12; it doesn't change. Integers should be immutable. The point (10, 30) is the point (10, 30); it doesn't change. Points should be immutable. The string "abc" is the string "abc"; it doesn't change. Strings should be immutable. The list (10, 20, 30) doesn't change. And so on.

Sometimes the type represents things that do change. Mary Smith's last name is Smith, but tomorrow she might be Mary Jones. Or Miss Smith today might be Doctor Smith tomorrow. The alien has fifty health points now but has ten after being hit by the laser beam. Some things are best represented as mutations.

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

Not as such. As I mentioned before though, one of the nice things about immutable values is that something you can re-use parts of them without making copies. So in that sense, memory allocation can be very different.

 

Change the value of a property of a struct in C# [duplicate]

问题:

This question already has an answer here:

I was reading a book and found out that structs are actually immutable objects. But they have getters and setters. I was wondering if a property of structs can be changed after it has been created.

public struct Test { public string str {get; set; } public int int1 {get; set; } }

Can the values of 'str' and 'int1' be changed once they have been assigned a value?

 

解答:

structs are not automatically immutable, but because of certain issues it is strongly recommended to make them immutable yourself. You may be thinking of a similar problem where you can't change the properties of a struct if the struct is itself being accessed as a property (one of the reasons why it is recommended to make them immutable.) Using your Test example:

public struct Test { public string str { get; set; } public int int1 { get; set; } } // This works: Test value = new Test(); value.str = "asdf"; value.int1 = 5; // But this does NOT work. Test TestProperty { get; set; } TestProperty.str = "asdf"; TestProperty.int1 = 5;

The reason the second part doesn't work is that you are only getting a value copy by saying TestProperty, and then you set the value of the copy's properties, rather than the one in your object.

To make your struct immutable you would do something like this:

public struct Test { readonly string mStr; readonly int mInt1; public string str { get { return mStr; } } public int int1 { get { return mInt1; } } public Test(string pStr, int pInt1) { mStr = pStr; mInt1 = pInt1; } }

You can create instances of Test, you can read their properties, but you can't change their properties, except by making a new instance.

 

Accessing and changing structs as property vs as field

问题

Ok, I'll start my question saying that I understand the evil behind mutable structs, but I'm working with SFML.net and using a lot of Vector2f and such structs.

What I don't get it is why I can have, and change the values of, a field in a class and can't do the same with a property, in the very same class.

Take a look at this code:

using System;

namespace Test { public struct TestStruct { public string Value; } class Program { TestStruct structA; TestStruct structB { get; set; } static void Main(string[] args) { Program program = new Program(); // This Works program.structA.Value = "Test A"; // This fails with the following error: // Cannot modify the return value of 'Test.Program.structB' // because it is not a variable //program.structB.Value = "Test B";  TestStruct copy = program.structB; copy.Value = "Test B"; Console.WriteLine(program.structA.Value); // "Test A" Console.WriteLine(program.structB.Value); // Empty, as expected } } }

note: I'll build my own classes to cover the same functionality and keep with my mutability, but I can't see a technical reason why I can do one and can't do other.

 

 解答:

When you access a field, you are accessing the actual struct. When you access it through property, you call a method that returns whatever is stored in the property. In the case of a struct, which is a value type, you will get back a copy of the struct. Apparently that copy is not a variable and cannot be changed.

Section "1.7 Structs" of the C# language specification 5.0 says:

With classes, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other.

That explains that you will receive a copy of the struct and not be able to modify the original struct. However, it doesn't describe why it isn't allowed.

Section "11.3.3" of the specifcation:

When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. If the instance expression is classified as a value, a compile-time error occurs. This is described in further detail in §7.17.1.

So the returned "thing" from the get accessor is a value and not a variable. That explains the wording in the error message.

The specification also contains an example in section 7.17.1 that is nearly identical to your code:

Given the declarations:

struct Point
{ int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } struct Rectangle { Point a, b; public Rectangle(Point a, Point b) { this.a = a; this.b = b; } public Point A { get { return a; } set { a = value; } } public Point B { get { return b; } set { b = value; } } }

in the example

Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;

the assignments to p.X, p.Y, r.A, and r.B are permitted because p and r are variables. However, in the example

Rectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;

the assignments are all invalid, since r.A and r.B are not variables.

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!