什么时候应该在C#中使用结构而不是类?
什么时候应该在C#中使用struct而不是class?我的概念模型是,当项目仅仅是一组值类型时,会使用结构。一种在逻辑上将它们结合在一起形成一个有凝聚力的整体的方法 我遇到了这些规则:什么时候应该在C#中使用结构而不是类?,c#,struct,C#,Struct,什么时候应该在C#中使用struct而不是class?我的概念模型是,当项目仅仅是一组值类型时,会使用结构。一种在逻辑上将它们结合在一起形成一个有凝聚力的整体的方法 我遇到了这些规则: 结构应该表示单个 价值观 结构应该有内存 占用空间小于16字节 之后不应更改结构 创造 这些规则有效吗?结构在语义上是什么意思 当需要值语义而不是引用语义时,请使用结构 编辑 不知道为什么人们会对此投反对票,但这是一个有效的观点,并且是在op澄清他的问题之前提出的,这是结构的最基本原因 struct Poin
- 结构应该表示单个 价值观
- 结构应该有内存 占用空间小于16字节
- 之后不应更改结构 创造
这些规则有效吗?结构在语义上是什么意思 当需要值语义而不是引用语义时,请使用结构 编辑 不知道为什么人们会对此投反对票,但这是一个有效的观点,并且是在op澄清他的问题之前提出的,这是结构的最基本原因
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
如果需要引用语义,则需要类而不是结构 只要您:
然而,需要注意的是,结构(任意大)的传递比类引用(通常是一个机器字)更昂贵,因此类在实践中可能会更快 我认为一个好的第二近似值是“从不”
如果你迫切需要PARF,考虑它们,但总是要测量。
结构对于数据的原子表示是很好的,其中所述数据可以由代码复制多次。克隆一个对象通常比复制一个结构要昂贵,因为它涉及到分配内存、运行构造函数以及在处理完它后取消分配/垃圾回收。
在需要使用-通常用于PInvoke显式指定内存布局的情况下,需要使用“结构”编辑:注释指出,可以将类或结构与StructLayoutAttribute一起使用,这当然是正确的。实际上,您通常会使用一个结构—它是在堆栈上分配的,而不是在堆上分配的,如果您只是将一个参数传递给一个非托管方法调用,这是有意义的。首先:互操作场景,或者当您需要指定内存布局时
第二:当数据几乎与引用指针大小相同时。除了运行时直接使用的valuetypes和其他用于PInvoke目的的各种valuetypes之外,您只能在两种情况下使用valuetypes
我使用structs打包或解包任何类型的二进制通信格式。这包括读取或写入磁盘、DirectX顶点列表、网络协议或处理加密/压缩数据
在这种情况下,你列出的三条指导方针对我没有用处。当我需要以特定的顺序写出400字节的内容时,我会定义一个400字节的结构,我会用它应该具有的任何不相关的值来填充它,我会以当时最有意义的方式来设置它。(好吧,四百字节可能会很奇怪——但当我以编写Excel文件为生的时候,我处理的是多达40字节的结构,因为这就是一些BIFF记录的大小。)不,我不完全同意这些规则。它们是考虑性能和标准化的良好准则,但不考虑可能性。 正如你在回答中看到的,有很多创造性的方法来使用它们。因此,这些指导原则必须是这样的,始终是为了性能和效率 在本例中,我使用类以更大的形式表示真实世界的对象,使用结构表示具有更精确用途的较小对象。就像你说的,“一个更具凝聚力的整体。”关键词是“凝聚力”。类将是更多面向对象的元素,而结构可以具有其中一些特性,尽管规模较小。国际海事组织 我经常在Treeview和Listview标记中使用它们,在这些标记中可以非常快速地访问常见的静态属性。我一直在努力以另一种方式获取这些信息。例如,在我的数据库应用程序中,我使用树状视图,其中有表、SP、函数或任何其他对象。我创建并填充我的结构,将其放入标记中,将其拉出,获取选择的数据,等等。我不会在课堂上这样做
我会尽量保持它们的小型化,在单实例的情况下使用它们,并防止它们发生变化。谨慎的做法是注意内存、分配和性能。测试是非常必要的。我很少使用结构来处理事情。但那只是我。这取决于我是否需要该对象为空
如其他答案所述,我将类用于现实世界的对象。我也有这样的想法,结构用于存储少量数据。我不同意原始帖子中给出的规则。以下是我的规则: 1) 当存储在阵列中时,可以使用结构来提高性能。(另见) 2) 在向C/C传递结构化数据或从C/C传递结构化数据的代码中需要它们++ 3) 除非需要,否则不要使用结构:
- 它们的行为不同于赋值和赋值下的“普通对象”(引用类型) 作为参数传递时,可能导致意外行为; 如果查看代码的人不这样做,这尤其危险 不知道他们正在处理一个结构
- 它们不能被继承
- 将结构作为参数传递比类更昂贵
- OP引用的源代码有一定的可信度……但是微软呢?对struct的使用持什么立场?我找了一些额外的,以下是我发现的:
如果
a型
int index = ...
int id = peopleArray[index].Id;
void Foo(ref Person person) {...}
...
Foo(ref peopleArray[index]);
class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
}
}
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
public struct MyPoint
{
public int X; // Value Type
public int Y; // Value Type
}
public class MyPointWithName
{
public int X; // Value Type
public int Y; // Value Type
public string Name; // Reference Type
}
public struct IntStruct {
public int Value {get; set;}
}
var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1; // A copy is made
var struct3 = struct2; // A copy is made
var struct4 = struct3; // A copy is made
var struct5 = struct4; // A copy is made
// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.
// Although structs are designed to use less system resources
// than classes. If used incorrectly, they could use significantly more.
public class IntClass {
public int Value {get; set;}
}
var class1 = new IntClass() { Value = 0 };
var class2 = class1; // A reference is made to class1
var class3 = class2; // A reference is made to class1
var class4 = class3; // A reference is made to class1
var class5 = class4; // A reference is made to class1
var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when
// struct1.Value is 0 and not 1
BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233542 Hz, Resolution=309.2584 ns, Timer=TSC
[Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
Clr : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
Core : .NET Core 4.6.25211.01, 64bit RyuJIT
Method | Job | Runtime | Mean | Error | StdDev | Min | Max | Median | Rank | Gen 0 | Allocated |
---------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|----------:|-----:|-------:|----------:|
TestListClass | Clr | Clr | 5.599 us | 0.0408 us | 0.0382 us | 5.561 us | 5.689 us | 5.583 us | 3 | - | 0 B |
TestArrayClass | Clr | Clr | 2.024 us | 0.0102 us | 0.0096 us | 2.011 us | 2.043 us | 2.022 us | 2 | - | 0 B |
TestListStruct | Clr | Clr | 8.427 us | 0.1983 us | 0.2204 us | 8.101 us | 9.007 us | 8.374 us | 5 | - | 0 B |
TestArrayStruct | Clr | Clr | 1.539 us | 0.0295 us | 0.0276 us | 1.502 us | 1.577 us | 1.537 us | 1 | - | 0 B |
TestLinqClass | Clr | Clr | 13.117 us | 0.1007 us | 0.0892 us | 13.007 us | 13.301 us | 13.089 us | 7 | 0.0153 | 80 B |
TestLinqStruct | Clr | Clr | 28.676 us | 0.1837 us | 0.1534 us | 28.441 us | 28.957 us | 28.660 us | 9 | - | 96 B |
TestListClass | Core | Core | 5.747 us | 0.1147 us | 0.1275 us | 5.567 us | 5.945 us | 5.756 us | 4 | - | 0 B |
TestArrayClass | Core | Core | 2.023 us | 0.0299 us | 0.0279 us | 1.990 us | 2.069 us | 2.013 us | 2 | - | 0 B |
TestListStruct | Core | Core | 8.753 us | 0.1659 us | 0.1910 us | 8.498 us | 9.110 us | 8.670 us | 6 | - | 0 B |
TestArrayStruct | Core | Core | 1.552 us | 0.0307 us | 0.0377 us | 1.496 us | 1.618 us | 1.552 us | 1 | - | 0 B |
TestLinqClass | Core | Core | 14.286 us | 0.2430 us | 0.2273 us | 13.956 us | 14.678 us | 14.313 us | 8 | 0.0153 | 72 B |
TestLinqStruct | Core | Core | 30.121 us | 0.5941 us | 0.5835 us | 28.928 us | 30.909 us | 30.153 us | 10 | - | 88 B |
[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkRef
{
public class C1
{
public string Text1;
public string Text2;
public string Text3;
}
public struct S1
{
public string Text1;
public string Text2;
public string Text3;
}
List<C1> testListClass = new List<C1>();
List<S1> testListStruct = new List<S1>();
C1[] testArrayClass;
S1[] testArrayStruct;
public BenchmarkRef()
{
for(int i=0;i<1000;i++)
{
testListClass.Add(new C1 { Text1= i.ToString(), Text2=null, Text3= i.ToString() });
testListStruct.Add(new S1 { Text1 = i.ToString(), Text2 = null, Text3 = i.ToString() });
}
testArrayClass = testListClass.ToArray();
testArrayStruct = testListStruct.ToArray();
}
[Benchmark]
public int TestListClass()
{
var x = 0;
foreach(var i in testListClass)
{
x += i.Text1.Length + i.Text3.Length;
}
return x;
}
[Benchmark]
public int TestArrayClass()
{
var x = 0;
foreach (var i in testArrayClass)
{
x += i.Text1.Length + i.Text3.Length;
}
return x;
}
[Benchmark]
public int TestListStruct()
{
var x = 0;
foreach (var i in testListStruct)
{
x += i.Text1.Length + i.Text3.Length;
}
return x;
}
[Benchmark]
public int TestArrayStruct()
{
var x = 0;
foreach (var i in testArrayStruct)
{
x += i.Text1.Length + i.Text3.Length;
}
return x;
}
[Benchmark]
public int TestLinqClass()
{
var x = testListClass.Select(i=> i.Text1.Length + i.Text3.Length).Sum();
return x;
}
[Benchmark]
public int TestLinqStruct()
{
var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
return x;
}
}
void AppendHello(StringBuilder builder)
{
builder.Append("hello");
}