限制对分部类的公共属性的访问

本文关键字:属性 访问 | 更新日期: 2023-09-27 18:04:08

背景

我有一个自动生成的部分类,其中一些公共属性表示来自其他嵌套模型的数据。例如:有床的房间:
// 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
}

问题

我想把嵌套模型的数据(在本例中是Bed)封装在一个对象中,但是限制对在容器模型中表示它的属性(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;

无效我的意思是我希望它导致编译错误,但如果这是不可能的,抛出一个警告,甚至阻止BedSize出现在智能感知(例如当使用[Browsable (false)]属性)可能几乎和我想要的一样好。

我当前的方法

目前,我可以使用嵌套类实现这一点,但我希望代码禁止或至少警告程序员不要使用一些自动生成的公共属性。例如

// 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
      }
}

我为什么要这个?

我知道你可能在想我到底为什么要这样的东西。好吧,在我的现实生活场景中,一些自动生成的公共属性的名称不像room.BedSize那样简短和直观,而是像:obj.DeviceManufacturerManufacturerTypeId这样的东西,我认为使用obj.Manufacurer.TypeId这样的东西会更清楚。

TL;博士:

我如何使私有在一个局部类的一些属性被定义为公共在另一个(自动生成)部分相同的局部类?如果这是不可能的,我怎样才能达到类似的效果?

限制对分部类的公共属性的访问

让自动生成的类程序的用户使用它的接口。与其公开类本身,不如公开它的接口,并只将您想要公开的方法放在那里:

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);
}

只要用户得到的是IHotelRom对象,而不是HotelRoom对象,如果没有显式强制转换,他们就不能访问HotelRoom的公共属性。

一种方法可能是使自动生成的属性成为接口的显式实现,该接口只向需要这些属性的代码公开。例如:

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);
    }
}

在上面的代码中(没有经过测试,但应该可以工作),没有访问IPersistableHotelRoom的代码将永远不会看到那里定义的属性。可以访问的代码可以通过对IPersistableHotelRoom进行强制转换来访问这些属性:

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