简体   繁体   English

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

[英]Practical differences between classes and structs in .net (not conceptual)?

Whenever I tried to search about differences between classes and structs in C# or .net, I ended up with the conceptual overview of the two things like value type or the reference type, where the variables are allocated etc. But I need some practical differences. 每当我尝试在C#或.net中搜索类和结构之间的差异时,我最终都会对值类型或引用类型这两个内容进行概念性概述,其中变量被分配等等。但我需要一些实际的差异。 I have found some like different behavior of assignment operator, having constructors etc. Can anybody provide some more practical differences which will be directly useful while coding? 我发现一些像赋值运算符的不同行为,有构造函数等。任何人都可以提供一些更实际的差异,这些差异在编码时会直接有用吗? Like the things works with one but not with other or same operation showing different behavior. 就像使用一个但不与其他或相同操作显示不同行为的东西一样。 And some common mistakes regarding these two. 关于这两个的一些常见错误。

Also please suggest where to consider using a struct instead of a class. 另外请建议在哪里考虑使用结构而不是类。 And where the structs should not be used. 并且不应该使用结构。

Edit: Do I have to call the constructor explicitly or just declaring a struct type variable will suffice?(Should I make it a new question?) 编辑:我是否必须显式调用构造函数或只是声明一个struct类型变量就足够了?(我应该把它作为一个新问题?)

OK, here are a few specific, practical differences: 好的,这里有一些具体的,实际的差异:

  • A variable can be null if it's a class , but is never null if it's a struct . 如果变量是 ,则变量可以为null ,但如果它是结构 ,则永远不为null。

  • default(T) is null for a class , but for a struct actually constructs a value (consisting of lots of binary zeros). 对于default(T)null ,但对于struct实际构造一个值(由许多二进制零组成)。

  • A struct can be made nullable by using Nullable<T> or T? 使用Nullable<T>T?可以使结构可为空T? . A class cannot be used for the T in Nullable<T> or T? 不能被用于TNullable<T>T? .

  • A struct always has a public default constructor (a constructor with zero parameters). struct 总是有一个公共默认构造函数(一个零参数的构造函数)。 The programmer cannot override this constructor with a custom implementation — it is basically “set in stone”. 程序员不能使用自定义实现覆盖此构造函数 - 它基本上是“一成不变”。 A class allows the programmer to have no default constructor (or a private one). 允许程序员没有默认构造函数(或私有构造函数)。

  • The fields in a class can have default values declared on them. 一个中的字段可以对他们声明的缺省值。 In a struct they can't. 结构中他们不能。

  • A class can inherit from another class, but a struct cannot be declared to derive from anything (it implicitly derives from System.ValueType ). 可以从另一个类继承,但是不能将结构声明为从任何东西派生(它隐式地从System.ValueType派生)。

  • It makes sense to use a class in object.ReferenceEquals() , but using a struct variable will always yield false. object.ReferenceEquals()使用是有意义的,但使用struct变量将始终产生false。

  • It makes sense to use a class in a lock() statement, but using a struct variable will cause very subtle failure. lock()语句中使用是有意义的,但使用struct变量将导致非常微妙的失败。 The code will not be locked. 代码不会被锁定。

  • On a 32-bit system, you can theoretically allocate an array of up to 536,870,912 references to a class , but for a struct you need to take the size of the struct into account because you are allocating actual instances . 在32位系统上,理论上可以为一个分配最多536,870,912个引用的数组,但是对于结构,您需要考虑结构的大小,因为您正在分配实际实例

Structs in a container can only be modified if the container is a built-in array: 只有容器是内置数组时,才能修改容器中的结构:

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.

For this reason, I strongly favour immutable structs: 出于这个原因,我强烈赞成不可变结构:

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

General observation: classes are usually better. 一般观察:课程通常更好。 Structs excel when you want to hold small, conceptually atomic data structures such as Point, Complex (number), Rational, etc. 当你想要保存小的,概念上原子的数据结构,如Point,Complex(number),Rational等时,结构优秀。

structs, as they are value types, are copied on assignment; 结构,因为它们是值类型,在赋值时被复制; if you create your own struct, you should make it immutable, see Why are mutable structs evil? 如果你创建自己的结构,你应该使它成为不可变的,看看为什么可变结构是邪恶的?

Sometimes you don't want what you're passing to be mutable, and since a mutable struct may just be pure evil, I'd steer clear of ever creating one :) Here's an example a situation: 有时你不希望你传递的东西是可变的,并且因为一个mutable结构可能只是纯粹的邪恶,所以我避免创造一个:)这是一个例子:

class Version: class版本:

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

struct Version: struct版本:

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

Now picture you called a method like this: 现在想象你调用了这样一个方法:

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

A struct is a Value type , meaning a copy gets passed into the method. struct是一种Value类型 ,意味着将副本传递给方法。 The class version means a reference gets passed into the method, you wouldn't want for example the account number to be changeable after the authorization passed, you want nothing to be changed in an operation like this...you want an immutable value type. 类版本意味着引用被传递到方法中,您不希望例如在授权传递后可以更改帐号,您希望在这样的操作中不需要更改任何内容...您想要一个不可变的值类型。 There's another question here asking why mutable structs are evil ...any operation where you wouldn't want anything affected by the reference object changing, would be a practical place where a struct may fit better. 这里有另一个问题,问为什么可变结构是邪恶的 ...任何你不希望任何受参考对象改变影响的操作,将是一个struct可能更适合的实际位置。

The example above may be somewhat silly, but the point is any sensitive operation where the passed in data shouldn't change in another thread or by any means really would be a place you look at passing by value. 上面的示例可能有些愚蠢,但重点是任何敏感操作,其中传入的数据不应在另一个线程中更改,或者通过任何方式实际上将是您查看通过值传递的位置。

Where they are allocated (heap vs. stack) is not something you really care about while you use them (not that you should disregard this - you should by all means study the differences and understand them). 分配它们的地方(堆与堆栈)不是你在使用它们时真正关心的东西(不是你应该忽略它 - 你应该通过各种方式研究差异并理解它们)。

But the most important practical difference you will come across the first time you decide to replace your class with a struct, is that structs are passed by value , while class instances are passed by reference . 但是,在您第一次决定用结构替换类时,最重要的实际区别是结构是通过传递 ,而类实例是通过引用传递

This means that when you pass a struct to a method, a copy of its properties is created (a shallow copy) and your method actually gets a different copy than the one you had outside the method. 这意味着当您将结构传递给方法时,会创建其属性的副本(浅拷贝),并且您的方法实际上获得的副本与您在方法之外的副本不同。 When you pass an instance of a class, only a reference to the same place in memory is passed to the method, and your method is then dealing with exactly the same data. 传递类的实例时,只会将对内存中相同位置的引用传递给该方法,然后您的方法将处理完全相同的数据。

For example, if you have a struct named MyStruct , and a class named MyClass , and you pass them to this method: 例如,如果您有一个名为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;
 }

when the method ends, your class' property will be incremented, but your struct's property will remain unchanged, because str variable inside the DoSomething method does not point to the same place in memory. 当方法结束时,你的类的属性将增加,但你的struct的属性将保持不变,因为DoSomething方法中的str变量不指向内存中的相同位置。

The singularly important practical difference is that structs are value types, whereas classes are reference types. 非常重要的实际区别在于结构是值类型,而类是引用类型。 That has a few implications. 这有一些影响。

First of all, structs are copied on assignment. 首先,结构被复制到赋值。 These two code blocks will have a different result (please note, normally you should neither use public fields nor mutable structs, I'm doing this for demonstration purposes only): 这两个代码块会有不同的结果(请注意,通常你既不应该使用公共字段也不应该使用可变结构,我这样做仅用于演示目的):

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"

X and Y are exactly the same, except that X is a struct . 除了Xstruct之外, XY完全相同。 The results of this are different because every time we assign an X , a copy is made, and if we change the copy then we aren't changing the original. 这样的结果是不同的,因为每次我们分配一个X ,一个副本,如果我们更改副本,那么我们不会更改原始。 On the other hand, when we assign the contents of y1 to y2 , all we've done is copied a reference; 另一方面,当我们将y1的内容分配给y2 ,我们所做的就是复制一个引用; both y1 and y2 refer to physically the same object in memory. y1y2指的是内存中物理上相同的对象。

The second consequence of structs being value types is generic constraints. 结构体作为值类型的第二个结果是通用约束。 If you want to pass in value types, the name of the constraint is literally "struct" or "class": 如果要传入值类型,则约束的名称实际上是“struct”或“class”:

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

The above will let you create a MyGeneric<int> or MyGeneric<X> , but not a MyGeneric<Y> . 上面的内容将允许您创建MyGeneric<int>MyGeneric<X> ,但不能创建MyGeneric<Y> On the other hand, if we change it to where T : struct , we're no longer allowed to create either of the first two, but MyGeneric<Y> is okay. 另一方面,如果我们将其更改为where T : struct ,我们不再允许创建前两个中的任何一个,但MyGeneric<Y>是可以的。

Last but not least, you need to use structs when writing interop, because with structs you're able to guarantee a specific arrangement in memory. 最后但并非最不重要的是,在编写互操作时需要使用结构,因为使用结构,您可以保证内存中的特定排列。

The link Tejs provided ( http://www.jaggersoft.com/pubs/StructsVsClasses.htm ) is a good explanation (although it is a bit out of date, particularly on the explanation of events). Tejs提供的链接( http://www.jaggersoft.com/pubs/StructsVsClasses.htm )是一个很好的解释(虽然它有点过时,特别是对事件的解释)。

The most import practical difference is that a struct is a value type, meaning it is passed by value rather than by reference. 最重要的实际区别是struct是一个值类型,这意味着它是通过而不是通过引用传递 What this really means is that when a struct is passed as an argument, it is actually passed by copy. 这真正意味着当一个struct作为参数传递时,它实际上是通过复制传递的。 As a result, operations on one instance of a struct do not affect other instances. 因此,对结构的一个实例的操作不会影响其他实例。

Consider the following code: 请考虑以下代码:

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

Because both ns1 and ns2 are of the NumberStruct value type, they each have their own storage location, so the assignment of ns2.Number does not affect the value of ns1.Number . 由于ns1ns2都是NumberStruct值类型,因此它们各自都有自己的存储位置,因此ns2.Number的赋值不会影响ns1.Number的值。 However, because nc1 and nc2 are both reference types, the assignment of nc2.Number does affect the value of nc1.Number because they both contain the same reference. 然而,由于nc1nc2都是引用类型,的分配nc2.Number不影响价值nc1.Number ,因为它们都含有相同的参考。

[Disclaimer: The above code and text taken from Sams Teach Yourself Visual C# 2010 in 24 Hours ] [免责声明:上述代码和文字摘自Sams教授自己的Visual C#2010 24小时 ]

Also, as others have already pointed out, structs should always be immutable. 此外,正如其他人已经指出的那样,结构应始终是不可改变的。 (Yes, in this example the struct is mutable but it was to illustrate the point.) Part of that means that structs should not contain public fields. (是的,在这个例子中,struct是可变的,但它是为了说明这一点。)部分原因是结构不应该包含公共字段。

Since structs are value types, you cannot inherit from a struct. 由于结构是值类型,因此不能从结构继承。 You also cannot derive a struct from a base class. 您也无法从基类派生结构。 (A struct can implement interfaces, however.) (但结构可以实现接口。)

A struct is also not allowed to have an explicitly declared public default (parameterless) contstructor. 结构也不允许具有显式声明的公共默认(无参数)contstructor。 Any additional constructors you declare must completely initialize all of the struct fields. 您声明的任何其他构造函数必须完全初始化所有struct字段。 Structs also cannot have an explicitly declared destructor. 结构也不能有显式声明的析构函数。

Since structs are value types, they shouldn't implement IDisposable and shouldn't contain unmanaged code. 由于结构是值类型,因此它们不应实现IDisposable ,也不应包含非托管代码。

Here's an interesting link: http://www.jaggersoft.com/pubs/StructsVsClasses.htm 这是一个有趣的链接: http//www.jaggersoft.com/pubs/StructsVsClasses.htm

For the most part though, there isn't much of a compelling reason to use structs when classes offer far more to the developer. 但是,在大多数情况下,当类为开发人员提供更多内容时,没有太多令人信服的理由使用结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM