我下面的课:
public class Humptydump
{
public Humptydump()
{ }
public Rectangle Rectangle { public get; private set; }
}
在这个类中,Rectangle 类来自 system.drawing,
我如何制作它以便人们无法访问矩形的方法,但可以获取矩形本身?
在您的情况下,它将“正常工作”。
既然Rectangle
是,您的struct
财产将返回一份. 因此,除非您公开允许这样做的方法,否则Rectangle
任何人都不可能直接修改您的。Rectangle
话虽如此,一般来说,如果不提供对类型上定义的方法的访问,就不可能提供对类型的访问。方法与类型一起使用。在这些情况下,唯一的选择是创建一个新类型,该类型公开您选择的数据,而不提供您希望公开的数据或方法,并提供对它的访问。
如果 rectangle 不是一个结构,那么可能的事情就是派生它并隐藏这些方法:
public class DerivedClass : BaseClass
{
new private SomeReturnType SomeMethodFromBaseClasse(SameParametersAsInBaseClassAndSameSignature
{
//this simply hides the method from the user
//but user will still have the chance to cast to the BaseClass and
//access the methods from there
}
}
您是在具体谈论Rectangle
对象,还是以更笼统的术语并仅以它为例?
如果您谈论的是更笼统的术语,那么这是在重构模式中经常出现的东西。这最常发生在对象上的集合中。例如,如果您公开 aList<T>
那么即使 setter 是私有的,那么人们仍然可以通过 getter 修改集合,因为他们这样做时实际上并没有设置集合。
为了解决这个问题,请考虑得墨忒耳定律。也就是说,当某人与对象公开的集合进行交互时,他们真的应该与对象本身进行交互吗?如果是这样,那么集合不应该被公开,而是对象应该公开它需要的功能。
因此,再次在集合的情况下,您可能会得到如下结果:
class SomeObject
{
private List<AnotherObject> Things;
public void AddAnotherObject(AnotherObject obj)
{
// Add it to the list
}
public void RemoveAnotherObject(AnotherObject obj)
{
// Remove it from the list
}
}
当然,您可能还希望公开对象本身的一些副本以供人们阅读,但不能修改。对于一个集合,我可能会做这样的事情:
public IEnumerable<AnotherObject> TheObjects
{
get { return Things; }
}
这样任何人都可以看到对象的当前状态并枚举它们,但他们实际上不能修改它。不是因为它没有setter,而是因为IEnumerable<T>
接口没有修改枚举的选项。只能一一列举。
对于您的情况Rectangle
(或类似的东西,无论如何都不是按值传递的结构),您会做一些非常相似的事情。存储一个私有对象并提供通过类本身修改它的公共功能(因为我们正在谈论的是类需要知道它的成员何时被修改)以及检查它的功能,而不能修改正在修改的内容检查。像这样的东西,也许:
class SomeObject
{
private AnotherObject Thing;
public AnotherObject TheThing
{
get { return Thing.Copy(); }
}
public void RenameThing(string name)
{
Thing.Name = name;
}
// etc.
}
在这种情况下,无需过多详细说明它是什么AnotherObject
(因此在某些方面考虑这是伪代码),检查内部对象的属性返回它的副本,而不是对实际对象的实际引用。对于值类型,这是语言的默认行为。对于引用类型,您可能需要在这和性能之间取得平衡(如果创建副本是一项繁重的操作)。
在这种情况下,您还需要小心使对象的界面不直观。消费代码可能期望能够修改被检查的内部对象,因为它公开了修改自身的功能。而且,确实,他们可以修改他们拥有的副本。你如何解决这个问题在很大程度上取决于对象的概念性质以及它们之间的关系,这是一个人为的例子并没有真正传达出来。您可能会创建一个自定义 DTO(甚至是一个结构),它只返回内部对象的可观察属性,从而更明显地表明它是副本而不是原始对象。您可能只是说它是智能感知评论中的副本。您可以创建单独的属性来返回内部对象的各个数据元素,而不是创建单个属性来返回对象本身。有很多选择,由您决定什么对您的对象最有意义。