Readonly struct vs classes

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
22
down vote

favorite
3












Assuming you only have immutable types
and you have all your code up to date to C# 7.3 and your methods are using the in keyword for inputs



Why would you ever use a class instead of a readonly struct?



The downside of using structs was that copying was expensive, but assuming you prevent any copy (defensive compiler copy or expressed by code), readonly structs allow you to only copy the reference (as classes do) and avoid heap allocation and pressure on the garbage collector.



Excluding special cases (which I guess it could be a very large object that won't fit on the stack) would you use readonly struct as first choice normally?



The case I am interested in is where they are used as data containers.







share|improve this question


















  • 1




    Because you want to access and edit the same object by reference?
    – MineR
    Aug 14 at 8:09







  • 10




    Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
    – Damien_The_Unbeliever
    Aug 14 at 8:11










  • Also no inheritance.
    – MineR
    Aug 14 at 8:14






  • 3




    You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
    – Dennis
    Aug 14 at 8:20






  • 1




    @jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
    – Damien_The_Unbeliever
    Aug 14 at 12:27














up vote
22
down vote

favorite
3












Assuming you only have immutable types
and you have all your code up to date to C# 7.3 and your methods are using the in keyword for inputs



Why would you ever use a class instead of a readonly struct?



The downside of using structs was that copying was expensive, but assuming you prevent any copy (defensive compiler copy or expressed by code), readonly structs allow you to only copy the reference (as classes do) and avoid heap allocation and pressure on the garbage collector.



Excluding special cases (which I guess it could be a very large object that won't fit on the stack) would you use readonly struct as first choice normally?



The case I am interested in is where they are used as data containers.







share|improve this question


















  • 1




    Because you want to access and edit the same object by reference?
    – MineR
    Aug 14 at 8:09







  • 10




    Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
    – Damien_The_Unbeliever
    Aug 14 at 8:11










  • Also no inheritance.
    – MineR
    Aug 14 at 8:14






  • 3




    You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
    – Dennis
    Aug 14 at 8:20






  • 1




    @jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
    – Damien_The_Unbeliever
    Aug 14 at 12:27












up vote
22
down vote

favorite
3









up vote
22
down vote

favorite
3






3





Assuming you only have immutable types
and you have all your code up to date to C# 7.3 and your methods are using the in keyword for inputs



Why would you ever use a class instead of a readonly struct?



The downside of using structs was that copying was expensive, but assuming you prevent any copy (defensive compiler copy or expressed by code), readonly structs allow you to only copy the reference (as classes do) and avoid heap allocation and pressure on the garbage collector.



Excluding special cases (which I guess it could be a very large object that won't fit on the stack) would you use readonly struct as first choice normally?



The case I am interested in is where they are used as data containers.







share|improve this question














Assuming you only have immutable types
and you have all your code up to date to C# 7.3 and your methods are using the in keyword for inputs



Why would you ever use a class instead of a readonly struct?



The downside of using structs was that copying was expensive, but assuming you prevent any copy (defensive compiler copy or expressed by code), readonly structs allow you to only copy the reference (as classes do) and avoid heap allocation and pressure on the garbage collector.



Excluding special cases (which I guess it could be a very large object that won't fit on the stack) would you use readonly struct as first choice normally?



The case I am interested in is where they are used as data containers.









share|improve this question













share|improve this question




share|improve this question








edited Aug 14 at 9:13

























asked Aug 14 at 8:07









user4388177

7651417




7651417







  • 1




    Because you want to access and edit the same object by reference?
    – MineR
    Aug 14 at 8:09







  • 10




    Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
    – Damien_The_Unbeliever
    Aug 14 at 8:11










  • Also no inheritance.
    – MineR
    Aug 14 at 8:14






  • 3




    You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
    – Dennis
    Aug 14 at 8:20






  • 1




    @jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
    – Damien_The_Unbeliever
    Aug 14 at 12:27












  • 1




    Because you want to access and edit the same object by reference?
    – MineR
    Aug 14 at 8:09







  • 10




    Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
    – Damien_The_Unbeliever
    Aug 14 at 8:11










  • Also no inheritance.
    – MineR
    Aug 14 at 8:14






  • 3




    You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
    – Dennis
    Aug 14 at 8:20






  • 1




    @jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
    – Damien_The_Unbeliever
    Aug 14 at 12:27







1




1




Because you want to access and edit the same object by reference?
– MineR
Aug 14 at 8:09





Because you want to access and edit the same object by reference?
– MineR
Aug 14 at 8:09





10




10




Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
– Damien_The_Unbeliever
Aug 14 at 8:11




Pick struct or class, first and foremost based on whether you want value semantics or reference semantics.
– Damien_The_Unbeliever
Aug 14 at 8:11












Also no inheritance.
– MineR
Aug 14 at 8:14




Also no inheritance.
– MineR
Aug 14 at 8:14




3




3




You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
– Dennis
Aug 14 at 8:20




You've taken limited range of use-cases, and tried to ask generic question, based on them. Reference types are not data containers only.
– Dennis
Aug 14 at 8:20




1




1




@jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
– Damien_The_Unbeliever
Aug 14 at 12:27




@jrh - yep, they will behave that way in a lot of ways. I can still pick 'em apart with good old ReferenceEquals though :-)
– Damien_The_Unbeliever
Aug 14 at 12:27












1 Answer
1






active

oldest

votes

















up vote
32
down vote



accepted










structs should not be looked on as "cheap objects"; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:



  • structs can't participate in polymorphism

  • you can't treat a struct as an instance of an interface without boxing it (caveat: "constrained call", but that only works in some scenarios)

  • a lot of library APIs won't work well (or possibly at all) with structs - they expect mutable POCOs; you'll probably want to use a library to get data from a database, serialize it, or render it in a UI - all of these things choke a bit with structs

  • structs don't work well with some patterns, such as tree or sibling relationships (a Foo can't contain a Foo if it is a struct) - there are others

  • structs, and immutable types generally, can be awkward to work with

Also, note that until very recently ("ref returns" and "ref locals") it was very hard to achieve some parts of "readonly structs allow you to only copy the reference"; this is now much simpler.



But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.



There are certainly times when structs are an amazing choice. It just isn't every time. I would however, support the notion that if you're going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you're doing it); mutable non-ref structs are a recipe for pain.






share|improve this answer






















  • All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
    – user4388177
    Aug 14 at 9:16











  • @user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
    – Marc Gravell♦
    Aug 14 at 9:21











  • That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
    – user4388177
    Aug 14 at 9:30










  • @user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
    – Marc Gravell♦
    Aug 14 at 9:34











  • Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
    – user4388177
    Aug 14 at 9:36










Your Answer





StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51836634%2freadonly-struct-vs-classes%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
32
down vote



accepted










structs should not be looked on as "cheap objects"; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:



  • structs can't participate in polymorphism

  • you can't treat a struct as an instance of an interface without boxing it (caveat: "constrained call", but that only works in some scenarios)

  • a lot of library APIs won't work well (or possibly at all) with structs - they expect mutable POCOs; you'll probably want to use a library to get data from a database, serialize it, or render it in a UI - all of these things choke a bit with structs

  • structs don't work well with some patterns, such as tree or sibling relationships (a Foo can't contain a Foo if it is a struct) - there are others

  • structs, and immutable types generally, can be awkward to work with

Also, note that until very recently ("ref returns" and "ref locals") it was very hard to achieve some parts of "readonly structs allow you to only copy the reference"; this is now much simpler.



But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.



There are certainly times when structs are an amazing choice. It just isn't every time. I would however, support the notion that if you're going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you're doing it); mutable non-ref structs are a recipe for pain.






share|improve this answer






















  • All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
    – user4388177
    Aug 14 at 9:16











  • @user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
    – Marc Gravell♦
    Aug 14 at 9:21











  • That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
    – user4388177
    Aug 14 at 9:30










  • @user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
    – Marc Gravell♦
    Aug 14 at 9:34











  • Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
    – user4388177
    Aug 14 at 9:36














up vote
32
down vote



accepted










structs should not be looked on as "cheap objects"; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:



  • structs can't participate in polymorphism

  • you can't treat a struct as an instance of an interface without boxing it (caveat: "constrained call", but that only works in some scenarios)

  • a lot of library APIs won't work well (or possibly at all) with structs - they expect mutable POCOs; you'll probably want to use a library to get data from a database, serialize it, or render it in a UI - all of these things choke a bit with structs

  • structs don't work well with some patterns, such as tree or sibling relationships (a Foo can't contain a Foo if it is a struct) - there are others

  • structs, and immutable types generally, can be awkward to work with

Also, note that until very recently ("ref returns" and "ref locals") it was very hard to achieve some parts of "readonly structs allow you to only copy the reference"; this is now much simpler.



But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.



There are certainly times when structs are an amazing choice. It just isn't every time. I would however, support the notion that if you're going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you're doing it); mutable non-ref structs are a recipe for pain.






share|improve this answer






















  • All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
    – user4388177
    Aug 14 at 9:16











  • @user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
    – Marc Gravell♦
    Aug 14 at 9:21











  • That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
    – user4388177
    Aug 14 at 9:30










  • @user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
    – Marc Gravell♦
    Aug 14 at 9:34











  • Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
    – user4388177
    Aug 14 at 9:36












up vote
32
down vote



accepted







up vote
32
down vote



accepted






structs should not be looked on as "cheap objects"; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:



  • structs can't participate in polymorphism

  • you can't treat a struct as an instance of an interface without boxing it (caveat: "constrained call", but that only works in some scenarios)

  • a lot of library APIs won't work well (or possibly at all) with structs - they expect mutable POCOs; you'll probably want to use a library to get data from a database, serialize it, or render it in a UI - all of these things choke a bit with structs

  • structs don't work well with some patterns, such as tree or sibling relationships (a Foo can't contain a Foo if it is a struct) - there are others

  • structs, and immutable types generally, can be awkward to work with

Also, note that until very recently ("ref returns" and "ref locals") it was very hard to achieve some parts of "readonly structs allow you to only copy the reference"; this is now much simpler.



But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.



There are certainly times when structs are an amazing choice. It just isn't every time. I would however, support the notion that if you're going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you're doing it); mutable non-ref structs are a recipe for pain.






share|improve this answer














structs should not be looked on as "cheap objects"; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:



  • structs can't participate in polymorphism

  • you can't treat a struct as an instance of an interface without boxing it (caveat: "constrained call", but that only works in some scenarios)

  • a lot of library APIs won't work well (or possibly at all) with structs - they expect mutable POCOs; you'll probably want to use a library to get data from a database, serialize it, or render it in a UI - all of these things choke a bit with structs

  • structs don't work well with some patterns, such as tree or sibling relationships (a Foo can't contain a Foo if it is a struct) - there are others

  • structs, and immutable types generally, can be awkward to work with

Also, note that until very recently ("ref returns" and "ref locals") it was very hard to achieve some parts of "readonly structs allow you to only copy the reference"; this is now much simpler.



But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.



There are certainly times when structs are an amazing choice. It just isn't every time. I would however, support the notion that if you're going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you're doing it); mutable non-ref structs are a recipe for pain.







share|improve this answer














share|improve this answer



share|improve this answer








edited Aug 14 at 11:51









Adrian

7,72711436




7,72711436










answered Aug 14 at 8:15









Marc Gravell♦

756k18820862509




756k18820862509











  • All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
    – user4388177
    Aug 14 at 9:16











  • @user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
    – Marc Gravell♦
    Aug 14 at 9:21











  • That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
    – user4388177
    Aug 14 at 9:30










  • @user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
    – Marc Gravell♦
    Aug 14 at 9:34











  • Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
    – user4388177
    Aug 14 at 9:36
















  • All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
    – user4388177
    Aug 14 at 9:16











  • @user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
    – Marc Gravell♦
    Aug 14 at 9:21











  • That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
    – user4388177
    Aug 14 at 9:30










  • @user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
    – Marc Gravell♦
    Aug 14 at 9:34











  • Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
    – user4388177
    Aug 14 at 9:36















All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
– user4388177
Aug 14 at 9:16





All valid points, thanks. Should I have to pick in a case where none of this apply would you suggest blindly going with readonly structs as default choice then? What are the cases you mentioned that make them hard to work with aside your points above?
– user4388177
Aug 14 at 9:16













@user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
– Marc Gravell♦
Aug 14 at 9:21





@user4388177 the reality that data often needs to change during your code is a big one :) especially when the data is in something like a List<T>, which doesn't have a "ref return" API (and cannot sensibly do so, because if somebody calls Add and forces the underlying array to grow, folks would expect the ref to point to the active array, but existing refs would actually be interior pointers into the old array). This means you can't just do myRef = myRef.WithSomeChangedProperty("foo"); (to overwrite the value)
– Marc Gravell♦
Aug 14 at 9:21













That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
– user4388177
Aug 14 at 9:30




That makes sense, but I'm more and more moving towards a more functional/immutable approach to the design. When I have to create a type and I want to enforce its immutability I believe readonly struct could be a good fit. Lists and arrays (even if you use immutable versions) most of the time fall into the "big objects" category so I wouldn't dream of using structs there.
– user4388177
Aug 14 at 9:30












@user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
– Marc Gravell♦
Aug 14 at 9:34





@user4388177 for lists, though - if GC is your concern, you might want to consider pools see here for context (look for "Here's the gist" for the actual code uesd)
– Marc Gravell♦
Aug 14 at 9:34













Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
– user4388177
Aug 14 at 9:36




Yep, we use ArrayPool, really useful. Thanks. I didn't know there was a GitHub issue about list pools, interesting. Although we ended up with some bugs in production with data from previous iterations XD.
– user4388177
Aug 14 at 9:36

















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51836634%2freadonly-struct-vs-classes%23new-answer', 'question_page');

);

Post as a guest













































































Comments

Popular posts from this blog

What does second last employer means? [closed]

Installing NextGIS Connect into QGIS 3?

One-line joke