C# net中类和结构之间的实际差异(不是概念上的)?

C# net中类和结构之间的实际差异(不是概念上的)?,c#,.net,class,struct,C#,.net,Class,Struct,每当我试图在C#或.net中搜索类和结构之间的差异时,我都会从概念上概述这两种类型,如值类型或引用类型,变量分配的位置等。但我需要一些实际的差异。我发现了一些类似赋值运算符的不同行为、具有构造函数等。有谁能提供一些更实际的差异,这些差异将在编码时直接有用?喜欢的东西与一个,但不与其他或相同的操作显示不同的行为。以及一些关于这两个方面的常见错误 也请建议在哪里考虑使用结构而不是类。以及不应使用结构的位置 编辑: 我必须显式调用构造函数还是只声明一个struct类型变量就足够了?(我应该把它变成一个

每当我试图在C#或.net中搜索类和结构之间的差异时,我都会从概念上概述这两种类型,如值类型或引用类型,变量分配的位置等。但我需要一些实际的差异。我发现了一些类似赋值运算符的不同行为、具有构造函数等。有谁能提供一些更实际的差异,这些差异将在编码时直接有用?喜欢的东西与一个,但不与其他或相同的操作显示不同的行为。以及一些关于这两个方面的常见错误

也请建议在哪里考虑使用结构而不是类。以及不应使用结构的位置

编辑:
我必须显式调用构造函数还是只声明一个struct类型变量就足够了?(我应该把它变成一个新问题吗?

这里有一个有趣的链接:


但是,在大多数情况下,当类为开发人员提供了更多功能时,没有什么令人信服的理由使用结构

只有当容器是内置数组时,才能修改容器中的结构:

struct Point { public int x, y; void Move(int dx, int dy) { x += dx; y += dy; } }
...
Point[] points = getPointsArray();
points[0].Move(10, 0) = 10;
// points[0].x is now 10 higher.
List<Point> points = getPointsList();
points[0].Move(10, 0);
// No error, but points[0].x hasn't changed.

一般观察:上课通常更好。当您想要保存小型的、概念上的原子数据结构(如点、复数、有理数等)时,结构会表现出色。

结构作为值类型,在赋值时复制;如果您创建自己的结构,您应该使其不可变,请参阅它们的分配位置(堆与堆栈)并不是您在使用它们时真正关心的事情(并不是说您应该忽略这一点-您应该尽一切努力研究它们的差异并理解它们)

但是,当您第一次决定用结构替换类时,您将遇到的最重要的实际差异是,结构是按值传递的,而类实例是按引用传递的

这意味着,当您将结构传递给方法时,将创建其属性的副本(浅层副本),并且您的方法实际获得的副本与方法外部的副本不同。当您传递一个类的实例时,只有对内存中相同位置的引用被传递给该方法,然后您的方法将处理完全相同的数据

例如,如果您有一个名为
MyStruct
的结构和一个名为
MyClass
的类,并将它们传递给此方法:

 void DoSomething(MyStruct str, MyClass cls)
 {
      // this will change the copy of str, but changes
      // will not be made to the outside struct
      str.Something = str.Something + 1;

      // this will change the actual class outside
      // the method, because cls points to the
      // same instance in memory
      cls.Something = cls.Something + 1;
 }

当方法结束时,类的属性将增加,但结构的属性将保持不变,因为
DoSomething
方法中的
str
变量不会指向内存中的同一位置。

非常重要的实际区别是结构是值类型,而类是引用类型。这有几个含义

首先,结构是在赋值时复制的。这两个代码块将产生不同的结果(请注意,通常不应使用公共字段或可变结构,我这样做只是为了演示):

X
Y
完全相同,只是
X
是一个
struct
。这样做的结果是不同的,因为每次我们指定一个
X
,就会生成一个副本,如果我们更改了副本,那么就不会更改原始副本。另一方面,当我们将
y1
的内容分配给
y2
时,我们所做的就是复制一个引用;
y1
y2
都指内存中物理上相同的对象

结构作为值类型的第二个结果是泛型约束。如果要传入值类型,约束的名称字面上是“struct”或“class”:

公共类MyGeneric
其中T:struct
{ ... }
通过以上操作,您可以创建
MyGeneric
MyGeneric
,但不能创建
MyGeneric
。另一方面,如果我们将其更改为
where T:struct
,我们将不再允许创建前两个中的任何一个,但是
MyGeneric
是可以的


最后但并非最不重要的一点是,在编写互操作时需要使用结构,因为使用结构可以保证内存中的特定安排。

有时您不希望传递的内容是可变的,而且由于
可变的
结构可能纯粹是邪恶的,我不想创造一个:)下面是一个例子:

版本:

class AccountInfo {
   public string OwnerName { get; set; }
   public string AccountNumber { get; set; }
}
struct AccountInfo {
   public string OwnerName;
   public string AccountNumber;
}
struct
版本:

class AccountInfo {
   public string OwnerName { get; set; }
   public string AccountNumber { get; set; }
}
struct AccountInfo {
   public string OwnerName;
   public string AccountNumber;
}
现在想象一下您调用的方法如下:

public bool TransferMoney(AccountInfo from, AccountInfo to, decimal amount)
{
   if(!IsAuthorized(from)) return false;
   //Transfer money
}
struct
是一个,意味着一个副本被传递到方法中。类版本意味着一个引用被传递到方法中,例如,您不希望在授权传递后帐户号可以更改,您不希望在这样的操作中更改任何内容…您想要一个不可变的值类型。。。如果您不希望任何操作受到引用对象更改的影响,那么这将是一个实用的位置,
struct
可能更适合此位置

上面的例子可能有点傻,但关键是任何敏感的操作,其中传入的数据不应该在另一个线程中更改,或者通过任何方式都确实是一个按值传递的地方。

Tejs提供的链接()是一个很好的解释(尽管它有点过时,特别是在解释事件方面)

最重要的实际区别是结构是一种值类型,这意味着它是通过值而不是引用传递的。这实际上意味着,当结构作为参数传递时,它实际上是通过副本传递的。因此,对结构的一个实例的操作不会影响其他实例

考虑以下代码:

struct NumberStruct
{
   public int Value;
}

class NumberClass
{
   public int Value = 0;
}

class Test
{
   static void Main() 
   {
      NumberStruct ns1 = new NumberStruct();
      NumberStruct ns2 = ns1;
      ns2.Value = 42;

      NumberClass nc1 = new NumberClass();
      NumberClass nc2 = nc1;
      nc2.Value = 42;

      Console.WriteLine("Struct: {0}, {1}", ns1.Value, ns2.Value);
      Console.WriteLine("Class: {0}, {1}", nc1.Value, nc2.Value);
   }
}
由于
ns1
ns2
都属于
NumberStruct
值类型,因此它们都有自己的存储位置,因此
ns2.Numb的分配
struct NumberStruct
{
   public int Value;
}

class NumberClass
{
   public int Value = 0;
}

class Test
{
   static void Main() 
   {
      NumberStruct ns1 = new NumberStruct();
      NumberStruct ns2 = ns1;
      ns2.Value = 42;

      NumberClass nc1 = new NumberClass();
      NumberClass nc2 = nc1;
      nc2.Value = 42;

      Console.WriteLine("Struct: {0}, {1}", ns1.Value, ns2.Value);
      Console.WriteLine("Class: {0}, {1}", nc1.Value, nc2.Value);
   }
}