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

时间:2010-05-01 13:21:50

标签: c# .net class struct

每当我尝试在C#或.net中搜索类和结构之间的差异时,我最终得到了两个方面的概念性概述,例如值类型或引用类型,其中变量被分配等等。但我需要一些实际差异。我发现一些像赋值运算符的不同行为,有构造函数等。任何人都可以提供一些更实际的差异,这些差异在编码时会直接有用吗?就像使用一个但不与其他或相同操作显示不同行为的东西一样。关于这两者的一些常见错误。

另外请建议在哪里考虑使用结构而不是类。并且不应该使用结构。

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

8 个答案:

答案 0 :(得分:10)

好的,这里有一些具体的,实际的差异:

  • 如果变量是,则变量可以是 null ,但如果它是 struct ,则永远不会为空。

  • 对于
  • default(T)null,但对于结构实际上构造了一个值(由许多二进制零组成)。

    < / LI>
  • 使用Nullable<T>T?可以使结构成为可空。 不能用于TNullable<T>中的T?

  • struct 始终有一个公共默认构造函数(一个零参数的构造函数)。程序员不能使用自定义实现覆盖此构造函数 - 它基本上是“一成不变”。 允许程序员没有默认构造函数(或私有构造函数)。

  • 中的字段可以在其上声明默认值。在结构中,他们不能。

  • 可以从其他类继承,但 struct 不能声明为从任何内容派生(它隐式派生自System.ValueType)。

  • object.ReferenceEquals()中使用是有意义的,但使用 struct 变量将始终产生错误。

  • lock()语句中使用是有意义的,但使用 struct 变量会导致非常微妙的失败。代码不会被锁定。

  • 在32位系统上,理论上可以将最多536,870,912个引用的数组分配给,但对于 struct < / strong>您需要考虑结构的大小,因为您正在分配实际的实例

答案 1 :(得分:2)

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

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.

出于这个原因,我强烈赞成不可变结构:

Point Move(int dx, int dy) { return new Point(x + dx, y + dy); }
...
points[0] = points[0].Move(10, 0); // Always works.

一般观察:课程通常更好。当你想要保存小的,概念上原子的数据结构,如Point,Complex(number),Rational等时,结构优秀。

答案 2 :(得分:1)

结构,因为它们是值类型,在赋值时被复制;如果您创建自己的结构,则应使其不可变,请参阅Why are mutable structs evil?

答案 3 :(得分:1)

有时你不希望你所传递的内容变得可变,并且因为mutable结构可能只是纯粹的邪恶,所以我避免创建一个:)这是一个例子:

class版本:

class AccountInfo {
   public string OwnerName { get; set; }
   public string AccountNumber { get; set; }
}

struct版本:

struct AccountInfo {
   public string OwnerName;
   public string AccountNumber;
}

现在想象你调用了这样一个方法:

public bool TransferMoney(AccountInfo from, AccountInfo to, decimal amount)
{
   if(!IsAuthorized(from)) return false;
   //Transfer money
}

structValue type,意味着将副本传递给该方法。类版本意味着引用被传递到方法中,您不希望例如在授权通过后可以更改帐号,您希望在这样的操作中不需要更改任何内容。你想要一个不可变的值类型。 There's another question here asking why mutable structs are evil ...任何你不希望任何受参考对象影响的事情的操作都是struct更适合的实际场所。

上面的示例可能有些愚蠢,但重点是任何敏感操作,其中传入的数据不应该在另一个线程中更改,或者通过任何方式确实是您看到的值传递的位置。

答案 4 :(得分:0)

分配它们(堆与堆栈)不是你在使用它们时真正关心的东西(不是你应该忽略它 - 你应该通过各种方法研究差异并理解它们)。

但是,当您决定用结构替换类时,您将遇到的最重要的实际区别是,结构通过值传递,而类实例通过传递参考

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

例如,如果您有一个名为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;
 }

当方法结束时,您的类'属性将递增,但您的struct的属性将保持不变,因为str方法中的DoSomething变量不指向记忆中的同一个地方。

答案 5 :(得分:0)

非常重要的实际区别在于结构是值类型,而类是引用类型。这有一些影响。

首先,结构被复制。这两个代码块会有不同的结果(请注意,通常你既不应该使用公共字段也不应该使用可变结构,我这样做只是为了演示目的):

struct X
{
    public int ID;
    public string Name;
}

X x1 = new X { ID = 1, Name = "Foo" };
X x2 = x1;
x2.Name = "Bar";
Console.WriteLine(x1.Name);    // Will print "Foo"

class Y
{
    public int ID;
    public string Name;
}
Y y1 = new Y { ID = 2, Name = "Bar" };
Y y2 = y1;
y2.Name = "Baz";
Console.WriteLine(y1.Name);    // Will print "Baz"
除了XY之外,

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

结构体作为值类型的第二个结果是通用约束。如果要传入值类型,则约束的名称实际上是“struct”或“class”:

public class MyGeneric<T>
    where T : struct
{ ... }

以上内容可让您创建MyGeneric<int>MyGeneric<X>,但不能创建MyGeneric<Y>。另一方面,如果我们将其更改为where T : struct,我们将不再允许创建前两个中的任何一个,但MyGeneric<Y>是可以的。

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

答案 6 :(得分:0)

Tejs提供的链接(http://www.jaggersoft.com/pubs/StructsVsClasses.htm)是一个很好的解释(虽然它有点过时,特别是对事件的解释)。

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

请考虑以下代码:

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);
   }
}

由于ns1ns2都属于NumberStruct值类型,因此它们各自都有自己的存储位置,因此ns2.Number的分配不会影响ns1.Number。但是,由于nc1nc2都是引用类型,nc2.Number的分配确实会影响nc1.Number的值,因为它们都包含相同的引用。

[免责声明:上述代码和文字取自Sams Teach Yourself Visual C# 2010 in 24 Hours]

此外,正如其他人已经指出的那样,结构应始终是不可变的。 (是的,在这个例子中,结构是可变的,但它是为了说明这一点。)部分原因是结构不应该包含公共字段。

由于结构是值类型,因此不能从结构继承。您也无法从基类派生结构。 (但结构可以实现接口。)

也不允许struct具有显式声明的public default(无参数)contstructor。您声明的任何其他构造函数必须完全初始化所有struct字段。结构也不能有明确声明的析构函数。

由于结构是值类型,因此它们不应实现IDisposable,也不应包含非托管代码。

答案 7 :(得分:-1)

这是一个有趣的链接:http://www.jaggersoft.com/pubs/StructsVsClasses.htm

但是,在大多数情况下,当类为开发人员提供更多内容时,使用结构的可能性并不大。