如何在C#中创建一个可私有设置的只读结构(如Size)?

Mar*_*eIV 3 c# struct structure const readonly

不确定是否可以这样做,但我需要计算并在基类中存储大小,然后将该结果作为只读方式公开给子类.通过将其隐藏在具有受保护的getter和私有setter的属性后面,使大小本身只读取是很容易的,就像这样......

private Size _someSize;
protected Size SomeSize
{
    get{ return _someSize; }
    private set{ _someSize = value; }
}
Run Code Online (Sandbox Code Playgroud)

然后从基类中,我可以像这样设置它......

SomeSize = new Size(23.0, 14.7);
Run Code Online (Sandbox Code Playgroud)

...但我无法从子类中执行此操作,因为setter对基类是私有的.

但是,我也不希望子类能够修改Size结构的成员.

更新

在子类中,如果您尝试编译此...

SomeSize.Width = 17.0;
Run Code Online (Sandbox Code Playgroud)

...您将收到错误'无法修改SomeSize的返回值,因为它不是变量',因此确实保护了基类中的值,正如我所希望的那样.

尽管如此,如果有人能弄清楚如何让getter返回一个真正的只读结构(如果这样的事情甚至可能,我怀疑),我会给你答案.当然,这个问题实际上并不需要,但要知道是否可以做到这一点仍然是一件好事.

Ste*_*art 5

您一定不能尝试编译它,因为您提出的建议已经满足您的需求.的Size类型是structure(值类型),而不是一个class(引用类型),所以属性getter将返回存储在_someSize的值,而不是对它的引用的副本.因此,如果子类实际上试图更改SomeSize.Width属性,它实际上不会触及私有_someSize变量.它只会更改返回的值的副本.但是,编译器认识到这样做是无效的,因此,它甚至不会让以下行编译:

SomeSize.Width = 17.0;
Run Code Online (Sandbox Code Playgroud)

您可以更改值并仍然可以进行编译的唯一方法是这样的:

Size temp = SomeSize;
temp.Width = 17.0;
Run Code Online (Sandbox Code Playgroud)

但是,就像我说的那样,因为那只是值的副本,它实际上不会改变SomeSize属性的值- 它只会改变值的值temp.

但是,如果Size类型是一个类,您仍然可以通过简单地返回对象的克隆而不是对原始对象的引用来实现相同类型的保护.例如,如果Size,实际上是一个看起来像这样的类:

public class MySize
{
    public MySize(float height, float width)
    {
        Height = height;
        Width = width;
    }

    public float Height { get; set; }
    public float Width { get; set; }

    public MySize GetCopy()
    {
        return (MySize)MemberwiseClone();
    }
}
Run Code Online (Sandbox Code Playgroud)

即使它的属性不是只读的,你仍然可以像这样制作一个伪的只读属性:

private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您确实希望返回对象的属性是只读的,那么唯一的方法是实现您自己的原始类型的只读版本.例如,该List<T>类型支持获取自身的只读版本的能力,以便您可以将其用于只读列表属性.因为List<T>具有内置功能,您可以轻松地执行以下操作:

private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
Run Code Online (Sandbox Code Playgroud)

但是,如您所见,该AsReadOnly方法不返回List<T>对象.它返回一个ReadOnlyCollection对象,这是一个全新的类型,可以自定义为列表的只读版本.换句话说,真正创建只读Size属性的唯一方法是创建自己的ReadOnlySize类型,如下所示:

public struct ReadOnlySize
{
    public ReadOnlySize(Size size)
    {
        _size = size;
    }

    private Size _size;

    public float Height 
    {
        get { return  _size.Height; } 
    }

    public float Width
    {
        get { return _size.Width; }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样制作你的只读属性:

private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}
Run Code Online (Sandbox Code Playgroud)