何时应该使用结构体而不是类?

18 浏览
0 Comments

何时应该使用结构体而不是类?

MSDN称,当你需要轻量级的对象时,应该使用structs。除此之外,还有哪些情况下struct比class更可取呢?

有些人可能忽略了以下几点:

  1. structs可以有方法。
  2. structs不能被继承。

我理解structs和classes之间的技术区别,只是对于何时使用struct还没有良好的感觉。

0
0 Comments

在Bill Wagner的书《effective c#》中,他提到了何时应该使用结构体而不是类。他通过以下原则得出结论:

1. 类型的主要责任是数据存储吗?

2. 它的公共接口是否完全由访问或修改其数据成员的属性定义?

3. 您确定您的类型永远不会有子类吗?

4. 您确定您的类型永远不会以多态方式对待吗?

如果对这4个问题的回答都是“是”,则应该使用结构体。否则,使用类。

那么...数据传输对象(DTO)应该是结构体吗?

我认为是的,如果满足上述4个标准。为什么数据传输对象需要以特定方式处理取决于您的情况。在一个项目中,我们的DTO中有常见的审计字段,因此编写了一个基础DTO,其他DTO从该基础DTO继承。

根据DTO的定义,如果您不从基类继承任何内容(这在DTO中很常见),则可以将其定义为结构体。

除了(2)之外,其他原则都非常好。需要看他的论证才能知道他在(2)中具体指的是什么,以及原因是什么。

关于这个问题的详细解释,请阅读他的书。抄袭书中的大部分内容是不公平的。

0
0 Comments

当应该使用结构体而不是类时会出现以下原因:

1. 结构体在逻辑上表示一个单一的值,类似于原始类型(整数、双精度等)。

2. 结构体的实例大小小于16个字节。

3. 结构体是不可变的。

4. 结构体不需要频繁进行装箱。

然而,有人对"不可变"这个条件的原因表示疑惑,为什么这是必要的呢?有人认为,这是因为如果结构体是不可变的,那么它具有值语义而不是引用语义就没有关系。只有在复制对象/结构体后对其进行更改时,这种区别才有意义。

在某些情况下,系统会临时复制一个结构体,然后允许将该副本按引用传递给修改它的代码;由于临时副本将被丢弃,所以更改将丢失。这个问题在结构体具有改变它的方法时尤为严重。然而,有人坚决不同意将可变性作为使某个类型成为类的原因,因为尽管c#和vb.net存在一些缺陷,但可变的结构体提供了无法通过其他方式实现的有用语义;没有语义上的理由优先选择不可变的结构体而不是类。

有人更喜欢Scott Meyers的原则。他明确偏离了.NET文档,因为他认为类型的使用是比类型的大小更好的因素。

有人对16字节的大小是否适用于64位代码表示疑惑。关于16字节的数字的来源,他们并不完全确定。

在设计JIT编译器时,微软决定对大小为16字节或更小的结构体进行代码优化;这意味着复制17字节的结构体可能比复制16字节的结构体慢得多。他们认为微软不太可能将这样的优化扩展到更大的结构体上,但重要的是要注意,虽然复制17字节的结构体可能比复制16字节的结构体慢,但有很多情况下,大型结构体可能比大型类对象更高效,并且结构体的相对优势随着结构体的大小增加而增加。

将相同的用法模式应用于大型结构体与应用于类的情况相比,可能会导致低效的代码,但正确的解决方法通常不是用类替换结构体,而是更有效地使用结构体;最重要的是,应避免按值传递或返回结构体。在合理的情况下,将一个具有4000个字段的结构体作为ref参数传递给一个修改字段的方法,比按值传递具有4个字段的结构体然后返回修改后的版本更便宜。

关于大型结构体何时更高效于大型类实例的"many cases"是什么?我只能想到一种可能,即必须强制执行不可变性,因此在执行任何操作时,"class"方法最终需要创建一个新的实例...我从未遇到过这种情况,但我很想知道它在何时发生。

我知道这是一个旧帖子,但是结构体被建议为不可变,因为它们通常(记住接口引用)是值类型对象,而值是被复制而不是更新的。虽然可以指向结构体并更改现有值;但是结构体仍然是复制而不是就地更新,就像类的工作方式一样。换句话说,这更安全,更符合现有结构体(例如int、char、DateTime等)的工作方式。

0
0 Comments

当应该使用结构体而不是类时?

我很惊讶之前的回答中没有提到我认为最关键的一点:

当我想要一个没有身份的类型时,我使用结构体。例如一个三维点:

public struct ThreeDimensionalPoint

{

public readonly int X, Y, Z;

public ThreeDimensionalPoint(int x, int y, int z)

{

this.X = x;

this.Y = y;

this.Z = z;

}

public override string ToString()

{

return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";

}

public override int GetHashCode()

{

return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);

}

public override bool Equals(object obj)

{

if (!(obj is ThreeDimensionalPoint))

return false;

ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;

return this == other;

}

public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)

{

return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;

}

public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)

{

return !(p1 == p2);

}

}

如果你有两个这个结构体的实例,你不关心它们是不是在内存中是一个单独的数据还是两个。你只关心它们所持有的值。

使用结构体的一个有趣的原因。我曾经创建过具有类似于你所展示的GetHashCode和Equals方法的类,但是我总是不得不小心不要改变这些实例,如果我把它们用作字典的键。如果我将它们定义为结构体可能会更安全。(因为然后键将是在结构体成为字典键的那一刻字段的副本,所以如果我以后更改了原始值,键将保持不变。)

在你的例子中没问题,因为你只有12个字节,但要记住,如果该结构体中有很多字段超过16个字节,你必须考虑使用类并重写GetHashCode和Equals方法。

在DDD中的值类型并不意味着你在C#中一定要使用值类型。

0