简体   繁体   中英

In what ways does a C# struct containing nullable fields differ from a class and from a pure structure?

I am designing a class that will contain a somewhat too big number of DateTime fields. It will be convenient to use this set of fields separately in some functions. Also, the meaning and way of usage of this set - what I can describe as a "complex timestamp", fits value type semantics better than object semantics IMHO. So I think of uniting these fields in one structure. An important detail is that many of these fields can be null as by the business logic model semantics. I would also prefer if it and its members would not be ever passed outside the class by reference (though this is a subjective idea of mine, perhaps passing this by reference would do no harm ever actually).

For example (not exactly my case, but a seemingly relevant illustration)

public struct ProjectTaskDateTimeStamp
{
    DateTime  Drafted // I don't mind if this field is made nullable too perhaps
    DateTime? Proposed
    DateTime? Approved
    DateTime? Finished
    DateTime? Archived
}

I may also decide to make this-typed property of the class nullable too.

How bad/reasonable idea this is and why? Should I make this a class instead and why?

In what actual ways (including memory representation and management, behaviour of itself, its members and other language/runtime subjects that will be interacting with it and passing/sharing it among each other, language semantics) does a structure containing nullable fields (and assigned to a nullable variable perhaps) differ from a class and from a pure (containing value type fields only) structure?

How bad/reasonable idea this is and why?

I will ignore memory and representation issues for a moment. Semantically, it sounds to me like you would be better off declaring an enum of the timestamp descriptions:

enum TimestampType { Drafted, Proposed, Approved, Finished, Archived };

And then your data structure would basically be a dictionary of those:

public struct ProjectTaskDateTimeStamp
{
    Dictionary<TimestampType, DateTime> Times = new Dictionary<TimestampType, DateTime>();
}

There can be zero or one times for every timestamp type, which is analogous to your version with nullables.


If, for any reason, the above suggestion doesn't work for you:

Should I make this a class instead and why?

Yes:

  • A large struct is likely to degrade performance, because copying it around can involve multiple instructions. There are specialized instructions that are optimized for this, but they are not always emitted by the JIT.
  • C# semantics make handling mutable structs a bit tricky. As it stands right now, you haven't written it in a way that suggests it is immutable. You can make it immutable by turning the fields into readonly properties (properties with a get accessor only).
  • The memory layout of a nullable structure is just like that of the non-nullable version plus a boolean indicating whether there is a valid value. Compared to a structure that contains only non-nullable fields, a structure that contains only the corresponding nullable versions of the types will have an additional boolean in its memory layout for each of its nullable fields.

This is part of the declaration of Nullable<T> :

public struct Nullable<T> where T : struct
{
    private bool hasValue; 
    internal T value;
    ...
}

Don't be fooled by the fact that you can assign null to a nullable variable -- that's just C# syntactic sugar for calling its default constructor (which sets hasValue to false ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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