更改 C# 中结构体的属性值[重复]

问题描述 投票:0回答:4

我在读一本书,发现结构实际上是不可变的对象。但他们有 getter 和 setter。我想知道结构体的属性在创建后是否可以更改。

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

“str”和“int1”赋值后还可以更改吗?

c# struct immutability setter
4个回答
10
投票

结构可以是可变的,也可以是不可变的,但对于很多人来说,它们应该是不可变的。

您的示例是一个可变结构。

使用示例:

var t = new Test();
// t.str is null, and t.int1 is 0

t.str = "changed!"; // OK

var t2 = t;
t2.int1 = 42;
// t.int1 is still 0

var li = new List<Test> { t, t2, };
t.int1 = 666;  // OK, but copy in li is unaffected

li[0].int1 = 911;  // compile-time error, not a variable

var t3 = t2;
bool checkA = (t3 == t2);  // compile-time error, you did not overload operator ==
bool checkB = t3.Equals(t2);  // OK, true, ValueType class overrides Equals for you
bool checkC = t2.Equals(t);  // OK, false
bool checkD = object.ReferenceEquals(t, t);  // false, two distinct boxes
                                             // same as (object)t==(object)t

根据要求,这是一种使其

struct
不可变的方法:

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

    public Test(string str, int int1) : this()
    {
        this.str = str;
        this.int1 = int1;
    }
}
// if you introduce methods (other than constructors) that use the private setters,
// the struct will no longer be immutable

这是另一个:

public struct Test 
{
    readonly string m_str;
    readonly int m_int1;

    public string str { get { return m_str; } }
    public int int1 { get { return m_int1; } }

    public Test(string str, int int1)
    {
        m_str = str;
        m_int1 = int1;
    }
}

更新:

从2015年(C# 6.0)开始,后者可以写成:

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

    public Test(string str, int int1)
    {
        this.str = str;
        this.int1 = int1;
    }
}

在以后的版本中还有更多的可能性。我只会提到你可以写(自 2021 年 (C# 10) 起):

public readonly record struct Test(string str, int int1) 
{
}

record
这个词现在意味着该类型会自动覆盖或重载
Equals
GetHashCode
operator ==
ToString
Deconstruct
等内容。并且构造函数现在是primary构造函数。


4
投票

“str”和“int1”赋值后还可以更改吗?

是的,

struct
的属性可以更改。
struct
本身并不是一成不变的。

但是让它们不可改变也算是一个很好的设计了。

来自结构设计

X 不要定义可变值类型。

可变值类型有几个问题。例如,当属性 getter 返回值类型时,调用者会收到一个副本。由于副本是隐式创建的,开发人员可能不会意识到他们正在改变副本,而不是原始值。此外,某些语言(特别是动态语言)在使用可变值类型时存在问题,因为即使是局部变量,在取消引用时也会导致创建副本。


4
投票

struct
并不是自动不可变的,但由于某些问题,强烈建议您自己使它们不可变。您可能会想到一个类似的问题,如果结构本身作为属性进行访问,则无法更改结构的属性(这就是建议使它们不可变的原因之一。)使用您的
Test
示例:

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;

第二部分不起作用的原因是,您只是通过说

TestProperty
来获取值副本,然后设置副本属性的值,而不是对象中的值。

为了使你的结构不可变,你可以这样做:

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;
   }
}

您可以创建

Test
的实例,您可以读取它们的属性,但不能更改它们的属性,除非创建一个新实例。


1
投票

绝对可以将新的实例值重新分配给结构。不变性意味着当您认为要更改值时,会在内存中创建一个新实例,并且之前的副本将被垃圾回收。

请注意,结构作为一个整体是不可变的。反过来,结构字段可以原子地可变或不可变。对于您的示例,字符串字段是不可变的,而 int 则不是。

© www.soinside.com 2019 - 2024. All rights reserved.