结构 - 现实生活中的例子?

时间:2009-08-28 20:44:33

标签: c# types struct

这里有很多问题可以解决C#中Structs和Classes之间的差异,以及何时使用其中一个。 (一句话答案:如果你需要价值语义,请使用结构。)有很多关于如何选择其中一种的指导方针,其中大部分归结为:除非你满足这些特定要求,否则使用一个类,然后使用结构。

这对我来说都很有意义。

但是,我似乎无法找到任何在系统中使用结构的人的真实例子。我是(半)新的C#,我很难想象结构确实是正确的选择的具体情况(至少,我还没有遇到过。)

所以,我转向SO世界大脑。在某些情况下,您实际在系统中使用结构的情况下,哪些类不起作用?

21 个答案:

答案 0 :(得分:20)

一个班级仍然适用于它,但我能想到的一个例子就像一个点。假设它是一个x和y值,你可以使用一个struct。

struct Point {
    int x;
    int y;
}

在我看来,我更倾向于使用一对整数的更简单的表示,而不是在实际实体实际上没有太多(或任何)行为的情况下定义使用具有实例化的类。

答案 1 :(得分:17)

我使用结构来表示地理位置

struct LatLng
{
    public decimal Lattitude
    {
        get;
        set;
    }
    public decimal Longitude
    {
        get;
        set;
    }
}

这表示单个实体,例如我可以将2个LatLng一起添加或在此单个实体上执行其他操作。

MSDN-struct

  

结构类型适合   表示轻量级对象   作为点,矩形和颜色。   虽然有可能代表一个   作为一个类,结构更多   在某些情况下有效。对于   例如,如果声明一个数组   1000 Point对象,您将分配   用于引用每个内存的额外内存   宾语。在这种情况下,结构是   更便宜。

另外,如果你看一下原始类型 Int32,十进制,双重等,你会发现它们都是结构,这允许它们是值类型,同时允许它们实现某些关键接口。

答案 2 :(得分:13)

结构通常也用于图形/渲染系统。制作点/矢量结构有很多好处。

Rico Mariani发表了一篇精彩的quiz on value based programming。他讨论了在特定情况下偏好结构的许多理由,并在他的quiz results post中详细解释了它。

答案 3 :(得分:7)

Money结构可能是最常见的结构之一,但电话号码或地址也很常见。

public struct Money
{
    public string Currency { get; set; }
    public double Amount { get; set; }
}

public struct PhoneNumber
{
    public int Extension { get; set; }
    public int RegionCode { get; set; }
    //... etc.
}

public struct FullName
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
}

请记住,尽管在.NET中你的结构内存占用量不应超过16字节,因为如果它们变大,CLR必须分配额外的内存。

另外,因为结构在堆栈上“活动”(而不是作为引用类型的堆),如果需要实例化许多相同类型的对象,可以考虑使用结构。

答案 4 :(得分:5)

典型的例子是框架nullable types,例如int?。这些使用结构,因此它们保留了int的值语义,但提供了一种方法,使它们无需装箱就可以将它们转换为引用类型。

当您不想通过引用传递某些内容时,您将使用结构。假设您有一个数据集合,或者您希望按值传递的对象(即,您传递给它的任何东西都使用它自己的唯一副本,而不是对原始版本的引用)那么struct是正确的类型使用

答案 5 :(得分:3)

它们为Object.GetHashCode()提供了一个默认实现,因此当对象是要用作字典键的非引用类型的简单集合时,您可能希望使用结构而不是类。

它们对于PInvoke / interop或低级网络场景也很有用,在这些场景中,您需要精确控制数据结构的二进制布局。 (访问www.pinvoke.net获取大量需要结构的互操作代码)

但实际上,我自己从不使用它们。不要冒汗不要使用它们。

答案 6 :(得分:2)

基本上我尝试不使用它们。我发现他们让团队中的其他开发人员感到困惑,因此不值得付出努力。我只发现了一个使用它的情况,一个类似于自定义类的类型,我们使用代码生成器从XML生成。

答案 7 :(得分:1)

对我而言,关键是要定义是否要继续引用同一个对象。

当struct是另一个实体的一部分时,它会产生一个实体本身。

在上面的示例中,LatLong可以完美呈现。您需要将值从一个对象复制到另一个对象,而不是继续引用相同的对象。

答案 8 :(得分:1)

我经常使用结构来表示可能表示为枚举的域模型值类型,但需要任意数量的离散值,或者我希望它具有无法添加到枚举的其他行为(方法) ...例如,在最近的项目中,许多数据元素与特定日历月相关联,而不是与日期相关联。所以我创建了一个具有方法的CalendarMonth结构:

  • 静态CalendarMonth Parse(DateTime inValue);
  • static CalendarMonth Parse(string inValue);

和TryParse()方法,

  • static bool TryParse(string inValue,out CalendarMonth outVal);

和属性

  • int Month {get;组; }
  • int年{get;组; }
  • DateTime StartMonthLocal {get;组; }
  • DateTime StartMonthUTC {get;组; }
  • DateTime EndMonthLocal {get;组; }
  • DateTime EndMonthUTC {get;组; }

答案 9 :(得分:1)

我一直在使用结构实现大规模缓存和延迟要求的金融机构工作。基本上结构可以节省大量工作的垃圾收集器。

请参阅以下示例:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/ http://00sharp.wordpress.com/2013/07/04/a-case-for-the-structpart-2/

答案 10 :(得分:1)

我通常不关心商务应用中的“数据密度”。除非我特别想要值语义

,否则我通常会使用一个类

这意味着我正在预测一种情况,我希望比较其中的两个,如果它们具有相同的值,我希望它们显示为相同。对于类,这实际上是更多工作,因为我需要覆盖==,!=,Equals和GetHashcode,即使resharper为我做了,也是额外的不必要的代码。

所以在我看来,总是使用类,除非你知道你希望通过值比较这些东西(在这种情况下是组件值)

答案 11 :(得分:1)

我无法相信没有人提到过XNA:在XNA中,所有几乎都是struct。所以当你这样做时

Matrix rotation = Matrix.CreateRotationZ(Math.PiOver2);

你真的在创造一种价值型。

这是因为,与应用程序编程不同,垃圾回收器运行时几毫秒的停顿是不可接受的(渲染整个帧只需要16.6毫秒!),所以我们有为了尽可能避免分配,所以GC不必运行那么多。

这对于XBox 360来说是尤其是真实的,其中GC远不及PC上的质量 - 即使平均每帧一次分配也会扼杀性能!

答案 12 :(得分:1)

所以我认为你从未使用过DateTime(结构)。

答案 13 :(得分:0)

C#中的结构体的核心不过是用胶带粘在一起的一堆变量。如果想要特定类型的每个变量表示一堆独立但相关的变量(例如点的坐标)与胶带粘在一起,那么使用暴露字段结构而不是类通常会更好,无论是否“束”意味着两个或二十个。请注意,虽然Microsoft的struct-versus-class建议适用于封装单个值的数据类型,但对于其目的是封装独立但相关的值的类型,它应该被认为是不适用的。 变量独立的程度越大,使用暴露字段结构的优势就越大。

如果有人希望使用一个类来封装一堆自变量,那么有两种方法可以做到,这两种方法都不是非常方便。可以使用不可变类,在这种情况下,该类类型的任何非空存储位置将封装由其标识的实例所持有的值,并且可以将一个存储位置复制到另一个存储位置以使新的存储位置封装那些相同的值。遗憾的是,更改由存储位置封装的值之一通常需要构建一个新实例,该实例与旧实例一样,除非该值已更改。例如,如果一个变量pt的类型为Immutable3dPoint,而另一个希望将pt.X增加一个,则必须执行以下操作:pt = new Immutable3dPoint(pt.X+1, pt.Y, pt.Z);如果type只封装了三个值,但如果有很多值,那就太烦人了。

另一种基于类的方法是使用可变类;这通常要求确保类类型的每个存储位置都将Universe中任何位置的唯一引用保存到该类的实例。创建存储位置时,必须构造一个新实例并在那里存储引用。如果希望将值从存储位置P复制到存储位置Q,则必须将所有字段或属性从一个实例复制到另一个实例(可能通过使该类型实现{ {1}}方法,并说CopyFrom。请注意,如果有人说Q.CopyFrom(P);似乎有效,但将来修改Q=P;的尝试也会修改P和反之亦然。可变类可能有效,并且它们有时可以有效,但是很容易搞砸。

Exposed-field结构将不可变类的方便的值复制语义与可变类允许的方便的分段修改相结合。大型结构的复制速度比对不可变对象的引用要慢,但修改部分暴露字段结构的成本仅取决于修改的程度,而不取决于整体结构的大小。相比之下,更改封装在不可变类类型中的一个数据的成本将与总类大小成比例。

答案 14 :(得分:0)

我不确定这有多大用处,但我今天发现虽然你不能在结构中使用实例字段初始化器,但你可以在课堂上使用。

因此,以下代码将给出编译错误,但如果您将“struct”更改为“class”,则会编译。

    public struct ServiceType
    {
        public bool backEnd { get; set; }
        public bool frontEnd { get; set; }

        public string[] backEndServices = { "Service1", "Service2" };
        public string[] frontEndServices = { "Service3", "Service4" };
    }

答案 15 :(得分:0)

我已经给出了在其他地方使用结构的原因(When to use struct in C#),并且在实际项目中出于这些原因使用了结构:

如果我需要在数组中存储大量相同的项类型,我会选择使用结构,这可能发生在图像处理中。

需要使用结构在C#和C ++之间传递结构化数据。

除非我有充分的理由使用它们,否则我会尽量避免它们。

我知道有些人喜欢使用它们来实现值语义,但是我发现这种行为与类的“正常”赋值行为(在C#中)有很大不同,因为一个人发现自己遇到了难以追踪的错误不记得一个人正在分配的对象有这种行为,因为它是作为结构而不是类实现的。 (它不止一次发生在我身上,所以我发出这个警告,因为我实际上被C#结构的不当使用所灼伤。)

答案 16 :(得分:0)

我过去创建的是StorageCapacity。它表示0字节到N exabytes(可能已经更高到yottabyte,但exa似乎当时足够)。自从我在一家存储管理公司工作以来,这个结构很有意义。你会认为这很简单:一个带有StorageUnit(枚举)和数量(我使用十进制)的结构。但是当您添加转换,运算符和类以支持格式化,解析等时,它会累加起来。

抽象很有用,可以让你获取任何StorageCapacity并将其表示为字节,千字节等,而不必多次乘以或除以1024。

答案 17 :(得分:0)

在某些性能关键的情况下,结构(值类型,因此从堆栈分配)可能比类(引用类型,因此从堆分配)更好。 Joe Duffy的博客文章“A single-word reader/writer spin lock”展示了这一现实应用。

答案 18 :(得分:0)

我认为.Net Framework非常真实。请参阅“结构”下的列表:

System Namespace

答案 19 :(得分:0)

我唯一一次使用结构就是在构建Fraction结构时:

public struct Fraction
{
   public int Numerator {get;set;}
   public int Denominator {get; set;}
   //it then had a bunch of Fraction methods like Reduce, Add, Subtract etc...
}

我觉得它代表一个值,就像内置值类型一样,因此如果它的行为类似于值类型,那么编码就会更自然。

答案 20 :(得分:0)

基本上,我使用Structs来建模几何和数学数据,或者当我想要一个基于价值的数据结构时。