8

I like using structs a lot.

So after reading this article, are there any other concerns I should have against using them all the time?

See Also:

Community
  • 1
  • 1
matt_dev
  • 5,176
  • 5
  • 33
  • 44
  • Here's a dup which links to three more dups... – Dana Mar 12 '09 at 21:44
  • I wish to mark Sasha's edit as offensive!! – abatishchev Mar 12 '09 at 21:51
  • @Sasha: http://stackoverflow.com/questions/39323/tagging-questions-duplicate-in-stackoverflow – Michael Myers Mar 12 '09 at 21:53
  • Whoops, here are a few: http://stackoverflow.com/questions/543515/structs-vs-classes-in-c http://stackoverflow.com/questions/85553/when-should-i-use-a-struct-instead-of-a-class – Dana Mar 12 '09 at 21:54
  • 1
    I added some references which are extremely similar. I didn't call them duplicates because it's not exactly the same question, but it's close enough that I voted to close. – Michael Myers Mar 12 '09 at 22:02
  • 1
    @Dana: The link to the C++ question is irrelevant. Structs in C++ is quite different from structs in C#. – Guffa Mar 12 '09 at 22:10

7 Answers7

11

I almost never define custom structs. There just aren't that many natural value types around, IMO.

In particular, I would think very, very carefully before defining a mutable struct, especially if it mutates via an interface implementation. Mutable structs behave in ways which people don't expect at all, leading to code which is hard to understand.

I think it's worth reading "Choosing Between Classes and Structures" from "Design Guidelines For Developing Class Libraries".

In particular:

Do not define a structure unless the type has all of the following characteristics:

  • It logically represents a single value, similar to primitive types (integer, double, and so on).

  • It has an instance size smaller than 16 bytes.

  • It is immutable.

  • It will not have to be boxed frequently.

Do you really develop types with all of those characteristics frequently?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
11

You should make the following considerations about structs:

  • structs should be immutable (mutable structs are not intuitive and unpredictable)
  • structs always have a default (public parameterless) constructor that cannot be changed
  • struct size should not exceed 16 bytes
  • the Equals and GetHashCode methods should be overriden for better performance
  • implementing the IEquatable<T> interface is recommended
  • redefining and == and the != operators is also recommended
Michael Damatov
  • 15,253
  • 10
  • 46
  • 71
  • 4
    What is the reason for limiting the size to 16 bytes? – John B Mar 31 '09 at 17:00
  • As far as I am aware, 16 bytes is the size of the reference to a reference type. Therefor you get no memory benefits when passing structs around or using them. Although I will say. I dont understand all this you should use immutable struct stuff. Structs are just structs and if they are what you need, I think they are what you should use. – Barry Jones Nov 12 '09 at 10:05
  • 3
    Mutable structs: consider the following multable `struct Point { public int X, public int Y }`. When it's used in a collection (e.g. `List`) iterating and modifying it would not update the value in the collection: `foreach(var p in points /* points is Point[] */) { p.X = 3 }`, because getting an element from the collection just gives you a *copy* (or a clone) of the element. The same is valid for passing struct values as method arguments. – Michael Damatov Nov 12 '09 at 10:37
  • 1
    Hi Michael, it's not that I dont understand the issues (if you can call them that) with structs. What you have stated to me, is what I consider expected behaviour. Therefor, if it is expected I wouldn't expect the values to be modified and wouldn't be supriprised if they were not. – Barry Jones Nov 14 '09 at 09:27
  • 1
    @Barry: Suppose ClearX() takes a Point by reference and clears the X component, and PointList is a List. Even though the compiler won't allow one to write PointList[5].X=0, it will allow ClearX(PointList[5]); the latter code, however, will have no useful effect. IMHO, the expected behavior would probably be to disallow such code in the first place, but such code is permitted as a trap to the unawary. – supercat Jan 20 '11 at 20:59
  • @BarryJones The issue is for the people unaware of what structs actually are. Especially beginners can be like: 'Ohoow, if I change this `class` to `struct` it still compiles perfectly. And somewhere on the web I read that `struct` is faster, YAY!'. It's expected behavior if you're aware of it, but a lot of us aren't even aware that structs behave completely different from classes. – Aidiakapi Mar 05 '12 at 15:44
  • @Aidiakapi: Anyone who doesn't know what a `struct` is needs to learn. If I declare `public struct foo {public int a, public int b;}`, then `foo` is essentially pair of variables fastened together with duct tape. If I have a `foo` called `boz`, then `boz` is essentially a synonym for `int boz.a; int boz.b`;`, but with the added bonus that the variables can be treated as a unit *when convenient to do so*. While there are times it can be useful for structures to include code, the most notable use case for structures is for cases where what's needed is a group of variables stuck together. – supercat Jan 02 '13 at 16:25
  • @Aidiakapi: While there are use cases for "opaque" structure types, a structure type *is* fundamentally a group of variables stuck together with duct tape, and so if what's *needed* is a group of variables stuck together with duct tape trying to obscure things by wrapping things in properties and using `WithXX` methods adds confusion rather than clarity. – supercat Jan 02 '13 at 16:30
4

They don't fit into an Object Oriented programming paradigm like classes do. They are good for small data structures, but I use classes for anything beyond that.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
0

I think main purpose of a struct - to keep only variable types. If you keep some classes into struct - you're wrong.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
0

Use of structs should be limited to when all you really need is a small data structure. (as you read). I would only really use them for the smallest of datastructures like coordinates, offsets, and sometimes for graphics.

In many ways, you can think of structs in C# as being like scaled - down classes. They are basically the same as classes but designed more for cases where you simply want to group some data together. They differ from classes in the following ways: Some things to keep in mind

  • Structs are value types, not reference types. This means they are stored either in the stack or in- line (if they are part of another object that is stored on the heap) and have the same lifetime re-strictions as the simple data types.
  • Structs do not support inheritance.
  • There are some differences in the way constructors work for structs. In particular, the compiler always supplies a default no - parameter constructor, which you are not permitted to replace.
bdd
  • 3,436
  • 5
  • 31
  • 43
0

Ask yourself the following questions about the set of data you're modeling with a struct:

  • Might it ever need to have any get/set logic?
  • Might it ever need to store ANY logic that is endemic to the data?
  • Might it need to inherit another set of data?
  • Might another set of data need to inherit this one?

I think if you can heartily answer "no" to all these questions, then there's no good reason not to use a struct. I think people use static subclasses in certain situations where a struct would be more than good enough.

danieltalsky
  • 7,752
  • 5
  • 39
  • 60
  • 1
    Could you go into more detail about point 2? There's nothing wrong with a struct having logic associated with it. – Jon Skeet Mar 12 '09 at 22:04
  • Well, the key word here is "endemic" vs. associated. I guess this isn't that clear, but what I meant was: might your struct need methods? I mean, classes are basically structs + endemic actions (well, ok, they're more than that, but that's what I was getting at) – danieltalsky Mar 13 '09 at 17:30
0

A class is a lot simpler to implement correctly than a struct. If you implement a struct incorrectly it can give you some unexpected errors.

A struct should not be larger than 16 bytes, or you lose most of the performance benefits.

A struct is intended to be a value type, representing a single entity of some kind.

A struct should be immutable. That means that you never change one of the properties in a struct. If you want a struct value that is different you create a new value.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • Actually, a struct is very easy to implement correctly. If what one needs is a group of three `int` called `Moe`, `Larry`, and `Curly`, `public struct Stooge { public int Moe, Larry, Curly; }`. Maybe for convenience add a constructor: `public Stooge(int newMoe, int newLarry, int newCurly) {Moe = newMoe; Larry = newLarry; Curly = newCurly;}`. Trying to put too much logic into a struct can be problematic, but there's a simple solution: *don't*. Someone who tries to pound nails with a screwdriver will find it to be an inferior hammer, but that doesn't mean a screwdriver isn't a good tool. – supercat Jan 02 '13 at 16:40
  • @supercat: That's not a correctly implemented struct, that's just a struct that should be a class instead. A struct should represent a single entity, not be a container for several entities. – Guffa Jan 02 '13 at 18:45
  • If instances of `Stooge` should have no identity outside of the values they contain, such that an array of 100 `Stooge` instances would hold precisely the same information as 300 integers, then `Stooge` must be either a struct or an immutable class; while there are some cases where an exposed-field struct would be usable but an immutable class would be better, I would regard those as the exception rather than the rule. Given `Stooges[] Stooge`, if `Stooge` was a class, how would one change `Stooges[3].Moe` without affecting `AnyOtherStooge.Moe`? How much code would one have to examine... – supercat Jan 02 '13 at 19:04
  • ...to confirm that one was doing so correctly? If `Stooge` is defined as above, `Stooges[3].Moe = 27;` would do the job quite simply; knowing that `Stooges[]` is a `Stooge`, and `Moe` is a field of `Stooge` would be sufficient to define the behavior. If `Stooge` is an immutable class, one might be able to do `Stooges[3] = Stooges[3].WithMoe(27);`, but one would have to examine the `WithMoe` mehtod to confirm what it does, and examine the entire `Stooge` class to ensure it really is immutable, and that `WithMoe` copies all of its fields correctly. That's somehow an improvement? – supercat Jan 02 '13 at 19:08
  • If a structure is going to behave as something other than a group of variables bound together with duct tape, then it should probably be immutable. If, however, what one needs a group of variables stuck together with duct tape, which can be passed around as a group when convenient, but also accessed singly (e.g. the coordinates of a point), it seems much more reasonable to use a group of variables stuck together with duct tape (i.e. an exposed-field struct) than to try to make a class that kinda sorta behaves like one, or to bog down a struct so it behaves like such a class. – supercat Jan 02 '13 at 19:14
  • @supercat: What you are showing is exactly how you should **not** use a struct. – Guffa Jan 02 '13 at 22:37
  • If one wants to bind a group of variables together with duct tape (as with coordinates of a point, etc.) are you suggesting that a class is somehow better than a struct? The struct above allows two useful operations: (1) make a copy of its state; (2) given an instance, produce an instance in which one member is changed but the others are not, without disturbing any other instance. How would you write a class so that it could do likewise, how obvious would it be that the class would in fact behave as intended, and what real benefit would the class offer over the struct? – supercat Jan 03 '13 at 00:58
  • @supercat: Yes, a class should be used for that, not a struct. It's of course up to you how you write your code as long as you are developing on your own for yourself, but that is not the intended use of a struct. Please don't try to teach people to misuse structs that way. – Guffa Jan 03 '13 at 01:11
  • Should one use a mutable or immutable class? If the former, is there any practical way to avoid the cost of defensive cloning every time one wants to take a snapshot? If the latter, how should one write code so that the full side effects of `Stooges[3] = Stooges[3].WithLarry(27);` can be ascertained with certainty? If fields are `readonly`, I know no way to write the `WithXX` methods decently; and if they're not, I know of no way to be certain the class is immutable except by examining every single line of it. Seems like a lot of work. What's the payoff? – supercat Jan 03 '13 at 01:27
  • @supercat: Yes, programming involves work. If you want a certain behaviour, you have to create it. Using a struct instead of a class because it has some specific behaviour doesn't make it any less work, because implementing a struct properly is a lot more work than implementing a class properly. There is no inherent expectations on a class to work as a single entity, for example having a hash code implementation and equality comparison. – Guffa Jan 03 '13 at 15:32
  • If I have a variable of class type `Foo`, that variable refers to a single entity: an instance of `Foo`. If the purpose of the variable is to hold three integers independent from anything else in the universe, and if `Foo` is mutable, I must create my own instance and never expose it to anyone (which makes it hard to share the three numbers as a group). If the reference is exposed, those three numbers are "attached" to any other existing references--no longer independent of everything else in the universe. If `Foo` is immutable, changing any one variable requires making a whole new instance. – supercat Jan 03 '13 at 18:28
  • If I have a method which needs to return the minimum and maximum values in a set of `IComparable`, and return them as a `MinMax` declared using the above pattern, `var result = MySet.GetMinMax();` will effectively create variables `result.Minimum` and `result.Maximum`, and set them as appropriate. The application can then use `result.Mimimum` and `result.Maximum` as it would any other variables, without any extra overhead. Using a more "sophisticated" return type will add complexity and overhead, and I still don't see that you've identified anything one gets in return for it. – supercat Jan 03 '13 at 18:43
  • @supercat: You don't get anything in return for not misusing structs other than avoiding the drawbacks of misusing structs. By using structs where you shouldn't, you are creating types that doesn't work well because they are not properly implemented. If you just want a bunch of values coupled together you should definitely use a class, that's not what a struct is intended for. – Guffa Jan 03 '13 at 20:31