13

创建一个简单的数据容器类时,它应该是什么?

  • 类还是结构?
  • 可变还是不可变?
  • 有或没有非空构造函数?

上述示例:

struct MutableStruct
{
    public string Text { get; set; }
    public int Number { get; set; }
}

struct ImmutableStruct
{
    public string Text { get; private set; }
    public int Number { get; private set; }
    public ImmutableStruct(string text, int number)
        : this()
    {
        Text = text;
        Number = number;
    }
}

struct MutableStructWithConstructor
{
    public string Text { get; set; }
    public int Number { get; set; }
    public MutableStructWithConstructor(string text, int number)
        : this()
    {
        Text = text;
        Number = number;
    }
}
class MutableClass
{
    public string Text { get; set; }
    public int Number { get; set; }
}

class ImmutableClass
{
    public string Text { get; private set; }
    public int Number { get; private set; }
    public ImmutableClass(string text, int number)
    {
        Text = text;
        Number = number;
    }
}

class MutableClassWithConstructor
{
    public string Text { get; set; }
    public int Number { get; set; }
    public MutableClassWithConstructor(string text, int number)
    {
        Text = text;
        Number = number;
    }
}

我们应该选择一个高于另一个的任何充分理由?还是主要是主观偏好将它们分开?还是很大程度上取决于特定的用例?如果是这样,您应该在哪些用例中选择什么以及为什么?

4

8 回答 8

11

几乎总是一堂课;结构实际上应该只用于的东西- 例如,复数或货币类型/值对 - 并且几乎- 没有排除是不可变的。

如果您要进行数据绑定,那么无参数构造函数对可变数据很方便,因为这允许系统创建实例而无需您自己编写额外的代码。非空构造函数对于不可变数据非常重要。对于可变数据,对象初始化器大有帮助(尽管在验证等方面并不完全相同)

var obj = new Person {Name="Fred", DateOfBirth=DateTime.Today};

您的类型是否不可变取决于您;mutable 使得数据绑定和序列化更容易。通常,您倾向于在 .NET 中看到更多可变类型,但随着我们进入并行/多核时代,这种情况可能会发生变化。

于 2009-07-13T11:51:01.050 回答
3
  • 您应该几乎总是更喜欢类而不是结构。仅当对象表示(不可变)值时才使用结构。

  • 如果您需要更改对象并且这样做是安全的,则使其可变,否则使其不可变并使用克隆。

  • 如果对象在使用默认构造函数创建时处于有效状态,则可以。否则总是提供你自己的构造函数。

于 2009-07-13T11:51:45.667 回答
2

仅在需要值类型语义时才使用结构,并尽量避免使用可变结构。

除此之外,我认为这取决于个人或团队的偏好和您的特定要求。

(这些天我试图尽可能使用不可变对象,这几乎总是需要将值传递给构造函数。)

于 2009-07-13T11:52:08.257 回答
1

关于类与结构的几点:

类是按引用传递的,这意味着在函数之间传递相同的实例对象。如果您在方法 A 中创建 MyClass 并调用方法 B,则方法 B 会更改类并且不向方法 A 返回任何内容,则方法 A 会看到 MyClass 中所做的更改,因为它们引用的是同一个对象。引用通常是 int32,因此无论您的类有多大,调用方法 B 都会很快,因为它只传递 4 个字节。类依赖垃圾收集器来决定何时不再使用它(传递类的开销较小,但会增加垃圾收集器的开销)。

结构是按值传递的(或值类型)。这意味着整个结构在传递时会被复制。如果你的结构很大,这将需要很长时间。方法 B 中结构的更改不会显示在方法 A 中,除非它被返回(再次花费时间,因为它是按值传递的),并且方法 A 读取返回值。结构是在堆栈上创建的,并且没有垃圾收集开销(如果您检查 IL 代码,您可以看到这一点)。

与类相比,结构有许多限制,例如缺乏虚拟方法和其他多态功能。

最好使用类,除非您要在同一方法中快速创建和丢弃大量对象(由于垃圾收集而耗尽系统资源),在这种情况下,使用结构。

于 2009-07-13T13:38:07.843 回答
0

我通常使用通过构造函数设置/传递所有值的不可变类。

于 2009-07-13T11:51:21.863 回答
0

此外,根据经验,结构不应大于 16 字节——处理器缓存行的大小。

于 2009-07-13T12:02:43.320 回答
0

每当您需要提供对象“功能”而不仅仅是身份或状态(即“值”)时,请考虑使用类。

如前所述,结构是值类型,因此将在您使用它们的任何地方复制。此外,在实例化类与结构时,性能差异可以忽略不计。

于 2009-07-13T13:47:23.713 回答
0

看起来几乎所有时间结构都是对类的易用性和效率的不必要的干扰。

于 2009-07-13T15:13:03.677 回答