Struct inheritence

AS 211 Reputation points
2021-01-08T14:59:19.68+00:00

All,

I have a struct that I would like to define in a base class and access it from classes that inherit from it

I've seen that I can't use protected with a struct. I would appreciate help with two questions:

a) I think it is because Structs are value types and not reference types? I'm new to C# and keen to understand the reasons behind things rather than just fix them.

b) Is my only option to use a class, instead of the struct, in this situation? The structure only has three items within it so creating a class for that seems like quite a small class.

Thanks

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,996 questions
0 comments No comments
{count} votes

Accepted answer
  1. Michael Taylor 54,731 Reputation points
    2021-01-08T17:21:50.41+00:00

    First let's define the main accessibility options in C#.

    • public: anyone has access
    • private: only the owning type has access
    • protected: only the owning type and derived types have access
    • internal: only the assembly containing the definition has access

    Accessibility has nothing to do with what types/members it is applied to.

    1) Root types can only be public or internal. Protected and private don't make sense because these limit the type/member to the owning type and root types don't have one. Therefore C# doesn't allow these accessibilities. However if the type is nested in another type then it can be any accessibility.

    csharp
    //Root types can be public or internal only
    public class PublicClass {}      // Accessible by anyone
    internal class InternalClass {} // Accessible to assembly
    //protected class ProtectedClass {} //Compiler error - no owning type
    //private class PrivateClass {}  // Compiler error - no owning type
    
    class SomeClass
    {
       public class PublicClass {}      // Accessible by anyone
       internal class InternalClass {} // Accessible to assembly
       protected class ProtectedClass {} // Accessible to owning and derived types
       private class PrivateClass {}   // Accessible to owning type only
    }
    

    2) In general you should use a struct for small sets of related data. Structs should be immutable so once created you shouldn't be able to modify the data. The original guideline was around 64 bytes but that is just a suggestion and ref structs (added in C# 9) change the rules a little.

    structs, like all value types, cannot inherit from a custom type nor be inherited from. If you need to be able to create derived types or use a custom base type then you must use a class. Small classes are fine. The overhead is just when allocating and cleaning them up. Options, for example, tend to start out small and may be derived from so they tend to be classes. Things like an address or lat/long or name often contain multiple data points but don't make sense unless combined together so they can be a struct.

    So, in summary, if you need to inherit from a type then you must use a class. However this has nothing to do with using the type as a field in another type. If you want a base type to expose a property to derived types then use either a class or a struct. If the derived type must be able to set the property then ensure the setter is accessible to the derived type. If it is a struct type then it should be immutable so derived types would need to create a new instance. C# does not allow you to set the properties of a struct type stored in a property.

    csharp
    public struct LatLong
    {
       public LatLong ( double lat, double lon ) : this()
       {
          Latitude = lat;
          Longitude = lon;
       }
    
       public double Latitude { get; }
       public double Longitude { get; }
    }
    
    class SomeType
    {
       //Anybody can get latlong but cannot change it
       //Derived types can set a new latlong but not change existing one
       public LatLong Location { get; protected set; }
    }
    
    var instance = new SomeType();
    var ll = instance.Location;  //Anybody can get value
    //ll.Latitude = ll.Longitude = 10.0; //Compiler error, properties are read only as they should be
    //instance.Location = new LatLong();  //Compiler error, setter is protected
    
    class DerivedType: SomeType
    {
       public void SetLocation ( LatLong value )
       {
          //Allowed because derived types can set
          Location = value;
    
          //Compiler error - properties are read only as they should be
          //Location.Latitude = value.Latitude;
          //Location.Longitude = value.Longitude;
       }
    }
    

    Note that creating a well formed value type requires a lot more effort because you should implement equality, comparison, etc. Refer to MSDN docs for how to create a "correct" value type. But for simple types you can get away without it.

    Think of a value type as a single value that you can either get or set just like integrals or floats. The fact that it might have several child values that make up the whole value isn't relevant. The value type itself is "atomic". Classes don't work that way though so they are more flexible, if you need it.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.