简体   繁体   English

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

[英]How can I create a privately-settable read-only struct (such as Size) in C#?

Not sure if this can be done, but I need to calculate and store a size in a base class, then expose that result as read-only to subclasses. 不确定是否可以这样做,但我需要计算并在基类中存储大小,然后将该结果作为只读方式公开给子类。 Making the Size itself readonly is easy by hiding it behind a property with a protected getter and a private setter, like this... 通过将其隐藏在具有受保护的getter和私有setter的属性后面,使大小本身只读取是很容易的,就像这样......

private Size _someSize;
protected Size SomeSize
{
    get{ return _someSize; }
    private set{ _someSize = value; }
}

Then from within the base class, I can set it like this... 然后从基类中,我可以像这样设置它......

SomeSize = new Size(23.0, 14.7);

...but I can't do that from a subclass class because the setter is private to the base class. ...但我无法从子类中执行此操作,因为setter对基类是私有的。

However, I also don't want the subclass to be able to modify the members of the Size structure either. 但是,我也不希望子类能够修改Size结构的成员。

Update 更新

In the subclass, if you try and compile this... 在子类中,如果您尝试编译此...

SomeSize.Width = 17.0;

...you will get the error 'Cannot modify the return value of SomeSize because it is not a variable' so that does protect the value in the base class as I had hoped. ...您将收到错误'无法修改SomeSize的返回值,因为它不是变量',因此确实保护了基类中的值,正如我所希望的那样。

Still, if someone can figure out how to make the getter return a true read-only structure (if such a thing is even possible, which I doubt), I'll give you the answer. 尽管如此,如果有人能弄清楚如何让getter返回一个真正的只读结构(如果这样的事情甚至可能,我怀疑),我会给你答案。 Granted, that wasn't actually needed for this problem, but that would still be a good thing to know if it can be done. 当然,这个问题实际上并不需要,但要知道它是否可以完成仍然是一件好事。

You must not have tried to compile it, because what you have proposed already meets your needs. 您一定不能尝试编译它,因为您提出的建议已经满足您的需求。 The Size type is a structure (value type), not a class (reference type), so the property getter will return a copy of the value stored in _someSize, not a reference to it. Size类型是一个structure (值类型),而不是class (引用类型),因此属性getter将返回存储在_someSize中的值的副本,而不是对它的引用。 Therefore, if a sub class actually tried to change the SomeSize.Width property, it wouldn't actually be touching the private _someSize variable. 因此,如果子类实际上试图更改SomeSize.Width属性,它实际上不会触及私有_someSize变量。 It would just be changing the copy of that value that was returned. 它只会更改返回的值的副本。 The compiler, however, recognizes that doing so is invalid, therefore, it won't even let the following line compile: 但是,编译器认识到这样做是无效的,因此,它甚至不会让以下行编译:

SomeSize.Width = 17.0;

The only way you could change the value and still get it to compile would be like this: 您可以更改值并仍然可以进行编译的唯一方法是这样的:

Size temp = SomeSize;
temp.Width = 17.0;

However, like I said, since that's just a copy of the value, it won't actually change the value of the SomeSize property--it will only change the value of temp . 但是,就像我说的那样,因为它只是值的副本,它实际上不会改变SomeSize属性的值 - 它只会改变temp的值。

If, however, the Size type was a class, you could still accomplish the same kind of protection by simply returning a clone of the object rather than a reference to the original object. 但是,如果Size类型是一个类,您仍然可以通过简单地返回对象的克隆而不是对原始对象的引用来实现相同类型的保护。 For instance, if Size , was actually a class that looked like this: 例如,如果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();
    }
}

Even though it's properties are not read-only, you could still make a pseudo read-only property out of it like this: 即使它的属性不是只读的,你仍然可以像这样制作一个伪的只读属性:

private MySize _someSize;
protected MySize SomeSize
{
    get { return _someSize.GetCopy(); }
    private set { _someSize = value; }
}

However, if you really want the properties of the returned object to be read-only, the only way to do that is to implement your own read-only version of the original type. 但是,如果您确实希望返回对象的属性是只读的,那么唯一的方法是实现您自己的原始类型的只读版本。 For instance, the List<T> type supports the ability to get a read-only version of itself so that you can use that for read-only list properties. 例如, List<T>类型支持获取自身的只读版本,以便您可以将其用于只读列表属性。 Because List<T> has that built-in functionality, you can easily do something like this: 因为List<T>具有内置功能,所以您可以轻松地执行以下操作:

private List<string> _list = new List<string>();
public ReadOnlyCollection<string> List
{
    get { return _list.AsReadOnly(); }
}

However, as you can see, the AsReadOnly method doesn't return a List<T> object. 但是,如您所见, AsReadOnly方法不返回List<T>对象。 It instead returns a ReadOnlyCollection object which is a whole new type that is custom made as a read-only version of the list. 它改为返回一个ReadOnlyCollection对象,该对象是一个全新的类型,可以自定义为列表的只读版本。 So, in other words, the only way to truly make a read-only Size property would be to create your own ReadOnlySize type, like this: 换句话说,真正创建只读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; }
    }
}

And then you could make your read-only property like this: 然后你可以像这样制作你的只读属性:

private Size _someSize;
public ReadOnlySize SomeSize
{
    get { return new ReadOnlySize(_someSize); }
}

One of the major limitations of structure types in .net languages is that while the fields of a writable struct-type storage location (variable, field, parameter, array slot, etc.) are themselves writable storage locations (and if they are struct types, their fields are likewise writable storage locations), such access only works with struct type storage locations , and there is no means via which any type other than System.Array can expose a property that works like a storage location. .net语言中结构类型的一个主要限制是,可写结构类型存储位置(变量,字段,参数,数组插槽等)的字段本身是可写存储位置(如果它们是结构类型) ,它们的字段同样是可写存储位置),这种访问仅适用于结构类型的存储位置 ,并且除了System.Array之外的任何类型都不能通过这种方式公开像存储位置一样工作的属性

It is possible for a class to expose a privately-held storage location as a storage location under controlled circumstances, if it offers a method which can pass that storage location as a ref parameter to a supplied callback. 如果类提供了一种方法,可以将该存储位置作为ref参数传递给提供的回调,则类可以在受控环境下将私有存储位置公开为存储位置。

For example: 例如:

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

If one wished to add 5 to the width of a something which exposed such a method, one could then use 如果一个人希望在暴露这种方法的东西的宽度上增加5,那么就可以使用

thing.ActOnSize((ref Size sz) => sz.Width += 5);

If one had a local variable 'HeightAdder', and one wished to add that to the object's height, one could use 如果有一个局部变量'HeightAdder',并且希望将其添加到对象的高度,可以使用

thing.ActOnSize((ref Size sz, ref int adder) =>
  sz.Height += adder, ref HeightAdder);

Note that because the lambda expression as written would not capture any local variables, it could be evaluated as a static delegate (if the delegate had added HeightAdder , rather than ref parameter adder to the height, that would have required generating a closure to hold HeightAdder each time it entered scope, and would have required generating a delegate each time the method call is executed; passing the amount as a ref parameter avoids that overhead). 请注意,因为写入的lambda表达式不会捕获任何局部变量,所以可以将其计算为静态委托(如果委托已将HeightAdder添加到高度,而不是ref参数adder ,则需要生成一个闭包来保存HeightAdder每次进入作用域时,每次执行方法调用时都需要生成一个委托;将该数量作为ref参数传递可以避免这种开销。

A similar approach could be used by a class similar to List<T> or Dictionary<TKey,TValue> to allow a callback method to act directly on a list slot or dictionary entry, if the access method includes a parameter for the index or key. 类似于List<T>Dictionary<TKey,TValue>的类可以使用类似的方法Dictionary<TKey,TValue>以允许回调方法直接作用于列表槽或字典条目,如果访问方法包括索引或键的参数。

One nice feature of this approach is that it allows collections to provide for styles of concurrent access that are otherwise difficult. 这种方法的一个很好的特性是它允许集合提供难以进行的并发访问的样式。 If the things in the collection are of a type which can be used with Interlocked methods (or are exposed-structure types whose fields are usable with such methods), client code may use such methods to perform thread-safe atomic operations on the underlying data. 如果集合中的东西是可以与Interlocked方法一起使用的类型(或者是其字段可用于此类方法的公开结构类型),则客户端代码可以使用此类方法对基础数据执行线程安全的原子操作。 If the things in a collection are not of such a type, the collection may be able to implement locking somewhat more safely than would be possible with other approaches. 如果集合中的东西不是这种类型的东西,那么集合可能能够比其他方法更安全地实现锁定。 For example, a ConcurrentIndexedSet<T> might have a family of ActOnItem(int item, int timeout, ActByRef<T> proc) ; 例如, ConcurrentIndexedSet<T>可能有一个ActOnItem(int item, int timeout, ActByRef<T> proc)ActOnItem(int item, int timeout, ActByRef<T> proc) ; methods which will invoke proc on an item within a lock (trusting that the client-supplied proc can be trusted to return in a reasonable timeframe). 将在锁内的项上调用proc方法(信任可以信任客户端提供的proc在合理的时间范围内返回)。 While such code couldn't guard against the possibility of proc getting deadlocked or otherwise waylaid, it could ensure that the lock will get released before returning control to the calling code. 虽然这样的代码无法防止proc陷入死锁或以其他方式运行的可能性,但它可以确保在将控制权返回给调用代码之前锁定将被释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM