如何在C#中创建可私人设置的只读结构(如大小)?

如何在C#中创建可私人设置的只读结构(如大小)?,c#,struct,constants,structure,readonly,C#,Struct,Constants,Structure,Readonly,不确定是否可以这样做,但我需要计算并在基类中存储一个大小,然后将结果以只读方式公开给子类。通过使用受保护的getter和私有setter将其隐藏在属性后面,使大小本身成为只读是很容易的,如下所示 private Size _someSize; protected Size SomeSize { get{ return _someSize; } private set{ _someSize = value; } } SomeSize = new Size(23.0, 14.7);

不确定是否可以这样做,但我需要计算并在基类中存储一个大小,然后将结果以只读方式公开给子类。通过使用受保护的getter和私有setter将其隐藏在属性后面,使大小本身成为只读是很容易的,如下所示

private Size _someSize;
protected Size SomeSize
{
    get{ return _someSize; }
    private set{ _someSize = value; }
}
SomeSize = new Size(23.0, 14.7);
然后在基类中,我可以这样设置它

private Size _someSize;
protected Size SomeSize
{
    get{ return _someSize; }
    private set{ _someSize = value; }
}
SomeSize = new Size(23.0, 14.7);
…但我不能在子类中这样做,因为setter对基类是私有的

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

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

SomeSize.Width = 17.0;
…您将得到错误“无法修改某个大小的返回值,因为它不是变量”,这样就可以像我希望的那样保护基类中的值


不过,如果有人能想出如何使getter返回真正的只读结构(如果这样的事情是可能的,我怀疑),我会给你答案。诚然,这个问题实际上并不需要这样做,但知道是否可以这样做仍然是件好事。

您一定没有尝试过编译它,因为您提出的方案已经满足了您的需要。
Size
类型是一个
结构
(值类型),而不是一个
(引用类型),因此属性getter将返回存储在_someSize中的值的副本,而不是对它的引用。因此,如果一个子类实际上试图更改
SomeSize.Width
属性,它实际上不会触及private\u SomeSize变量。它只是更改返回的值的副本。但是,编译器认识到这样做是无效的,因此,它甚至不会让以下行编译:

SomeSize.Width = 17.0;
只有这样,才能更改值并使其编译:

Size temp = SomeSize;
temp.Width = 17.0;
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();
    }
}
private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
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; }
    }
}
private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}
但是,正如我所说,由于这只是值的一个副本,它实际上不会更改
SomeSize
属性的值——它只会更改
temp
的值

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

Size temp = SomeSize;
temp.Width = 17.0;
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();
    }
}
private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
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; }
    }
}
private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}
即使它的属性不是只读的,您仍然可以将其制作为伪只读属性,如下所示:

Size temp = SomeSize;
temp.Width = 17.0;
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();
    }
}
private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
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; }
    }
}
private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}
但是,如果您确实希望返回对象的属性是只读的,那么唯一的方法就是实现您自己的原始类型的只读版本。例如,
List
类型支持获取自身的只读版本,以便您可以将其用于只读列表属性。由于
List
具有内置功能,因此您可以轻松执行以下操作:

Size temp = SomeSize;
temp.Width = 17.0;
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();
    }
}
private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
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; }
    }
}
private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}
然后您可以将只读属性设置为:

Size temp = SomeSize;
temp.Width = 17.0;
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();
    }
}
private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}
private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}
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; }
    }
}
private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}

.net语言中结构类型的主要限制之一是,虽然可写结构类型存储位置(变量、字段、参数、数组插槽等)的字段本身就是可写存储位置(如果它们是结构类型,它们的字段也是可写存储位置),这种访问只适用于结构类型的存储位置,除了
System.Array
之外,任何类型都无法公开与存储位置类似的属性

如果类提供了一种方法,可以将私有存储位置作为
ref
参数传递给提供的回调,则该类可以在受控环境下将私有存储位置公开为存储位置

例如:

public delegate void ActByRef<T1>(ref T1 p1);
public delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
public delegate void ActByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);

public void ActOnSize(ActByRef proc) 
  { proc( ref _someSize); }
public void ActOnSize<XT1>(ActByRef proc, ref XT1 xp1) 
  { proc( ref _someSize, ref xp1); }
public void ActOnSize<XT1,XT2>(ActByRef proc, ref XT1 xp1, ref XT2 xp2)
  { proc( ref _someSize, ref xp1, ref xp2); }
如果有一个局部变量“HeightAdder”,并且希望将其添加到对象的高度,则可以使用

thing.ActOnSize((ref Size sz, ref int adder) =>
  sz.Height += adder, ref HeightAdder);
请注意,由于编写的lambda表达式不会捕获任何局部变量,因此可以将其作为静态委托进行计算(如果代理在高度中添加了
HeightAdder
,而不是
ref
参数
adder
,则每次进入范围时都需要生成一个闭包来容纳
HeightAdder
,并且每次执行方法调用时都需要生成一个代理;将金额作为
r传递。)ef
参数避免了这种开销)

类似于
List
Dictionary
的类可以使用类似的方法,以允许回调方法直接作用于列表槽或字典条目,前提是访问方法包括索引或键的参数


这种方法的一个很好的特性是,它允许集合提供在其他方面很困难的并发访问样式。如果集合中的内容属于可以与
互锁
方法一起使用的类型(或者是公开的结构类型,其字段可以与此类方法一起使用),客户端代码可以使用这些方法对底层数据执行线程安全的原子操作。如果集合中的内容不是这种类型,则集合可以比其他方法更安全地实现锁定。例如,
ConcurrentIndexedSet
可能有一系列
ActionItem(int item,int timeout,ActByRef proc)
;将在锁内的项上调用
proc
的方法(相信可以信任客户端提供的
proc
在合理的时间范围内返回)。虽然这样的代码不能防止
proc
被死锁或以其他方式阻止的可能性,但它可以确保在将控制权返回给调用代码之前释放锁。

类大小也是由您实现的吗?可能使用base.new(基类构造函数)(不确切知道c#语法…)不,它是.NET框架的一部分。我知道我可以标记一个变量w