简体   繁体   中英

Restrict access to public properties of partial class

The context

I have an autogenerated partial class in which some public properties represent data from some other nested model. Eg a room containing a bed:

// Auto-generated class from DB schema
public partial class HotelRoom : BaseModel
{
    public int RoomNumber {get; set;}
    public string BedKind {get; set;}
    public int BedSize {get; set;}
    public bool BedHasBlankets {get; set;}

    // Methods and more DB-related logic here
}

The problem

I would like to encapsulate the data of the nested model (in this case, the Bed ) in a single object, but restricting the access to the properties that represent it in the container model ( HotelRoom ).

// Suppose we get a reference to a HotelRoom
HotelRoom room = HotelRoom.GetFromDB(someNumber);

// I want this to be valid ...
var size = room.Bed.Size;

// ...but this to be invalid
var size = room.BedSize;

By invalid I mean that I would like it to cause a compiling error, but if that is not possible at all, throwing a warning, or even preventing BedSize from appearing in IntelliSense (such as when using the [Browsable (false)] attribute) might be almost as good as what I want.

My current approach

Currently, I can achieve this using a nested class, but I would like the code to forbid or at least warn the programmers against using some of the autogenerated public properties. Eg

// My own partial class
public partial class HotelRoom : BaseModel
{
    // Here I would like to make BedKind, BedSize and BedHasBlankets private
    // or make something that has a similar effect as making them private

    // Then I can encapsulate those variables in this object whose class
    // has access to the private properties of HotelRoom, since it's nested
    private Bed bed;
    public Bed Bed
    {
      get
      {
        if(bed == null)
        {
          bed = new Bed(this);
        }
        return bed;
      }
    }

    public class Bed
    {
      private HotelRoom container;
      public string Kind { get{ return container.BedKind } }
      public int Size { get{ return container.BedSize } }
      // Maybe perform some data transformation:
      public bool NeedsBlankets { get{ return !container.BedHasBlankets;} }

      public Bed(HotelRoom container)
      {
        this.container = container
      }
}

Why do I want this?

I know you might be thinking why on earth would I want something like this. Well, in my real life scenario, some of the the auto-generated public properties' names are not as short and intuitive as room.BedSize , but rather things like: obj.DeviceManufacturerManufacturerTypeId which I think would be clearer to call using something like obj.Manufacurer.TypeId .

TL;DR:

How can I make private in a partial class some properties that are defined as public in another (autogenerated) part of the same partial class? If that isn't possible, how can I achieve a similar effect?

Make users of your autogenerated class program to its interface. Rather than exposing the class itself, expose its interface, and put there only the methods that you want to be exposed:

interface IBed {
    string Kind {get; set;}
    int Size {get; set;}
    bool HasBlankets {get; set;}
}
interface IHotelRoom {
    int RoomNumber {get; set;}
    IBed Bed {get;}
}
partial class HotelRoom : BaseModel, IHotelRoom {
    private class Bed : IBed {
        private HotelRoom room;
        internal Bed(HotelRoom room) {this.room = room;}
        public string Kind {
            get { return room.BedKind; }
            set { room.BedKind = value; }
        }
        ...
    }
    public IBed Bed {get;} = new Bed(this);
}

As long as the users get IHotelRom object, not HotelRoom , they have no access to public properties of HotelRoom without an explicit cast.

One approach might be making the autogenerated properties explicit implementations of an interface that is only exposed to the code that needs those properties. For example:

interface IPersistableHotelRoom { 
    int BedSize {get; set;}
}

public class HotelRoom : IPersistableHotelRoom {
    int IPersistableHotelRoom.BedSize { get; set; }

    public class Bed {
        private HotelRoom room; // set this in the nested class constructor, not shown
        public int Size { get { return room.BedSize; } }
    }

    public Bed Bed {get;}

    public HotelRoom() {
        this.Bed = new Bed(this);
    }
}

In the code above (not tested, but should work) code that doesn't have access to IPersistableHotelRoom will never see the properties defined there. Code that does have access to it can access those properties by doing a cast to IPersistableHotelRoom :

var foo = ((IPersistableHotelRoom)hotelRoom).BedSize;

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