如何将引用类型属性设置为“只读”

发布于 2024-07-16 05:40:17 字数 500 浏览 9 评论 0原文

我有一个类 Bar,其私有字段包含引用类型 Foo。 我想在公共属性中公开 Foo,但我不希望该属性的使用者能够更改 Foo...但是它应该可以在内部更改通过 Bar,即我无法使该字段只读

所以我想要的是:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

……这当然是无效的。 我可以只返回 Foo 的克隆(假设它是 IClonable),但这对消费者来说并不明显。 我应该将属性名称更改为 FooCopy 吗? 它应该是 GetCopyOfFoo 方法吗? 您认为最佳实践是什么? 谢谢!

I have a class Bar with a private field containing the reference type Foo. I would like to expose Foo in a public property, but I do not want the consumers of the property to be able to alter Foo... It should however be alterable internally by Bar, i.e. I can't make the field readonly.

So what I would like is:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

...which is of course not valid. I could just return a clone of Foo (assumming that it is IClonable), but this is not obvious to the consumer. Should I change the name of the property to FooCopy?? Should it be a GetCopyOfFoo method instead? What would you consider best practice? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

ㄟ。诗瑗 2024-07-23 05:40:17

听起来您正在寻找 C++ 中的“const”等价物。 这在 C# 中不存在。 没有办法表明消费者不能修改对象的属性,但其他东西可以(当然假设变异成员是公共的)。

您可以按照建议返回 Foo 的克隆,或者可能返回 Foo 的视图,如 ReadOnlyCollection 适用于集合。 当然,如果您可以使 Foo 成为不可变类型,那将使生活变得更简单...

请注意,使 字段 只读和使对象本身不可变之间存在很大差异。

目前,类型本身可以从两个方面改变事情。 它可以做:

_Foo = new Foo(...);

_Foo.SomeProperty = newValue;

如果它只需要能够做第二个,该字段可以是只读的,但仍然存在人们获取属性能够改变对象的问题。 如果它只需要执行第一个操作,并且实际上 Foo 要么已经是不可变的,要么可以变得不可变,那么您只需提供一个仅具有“getter”的属性就可以了。

了解更改字段的值(使其引用不同的实例)和更改字段引用的对象的内容之间的区别非常重要

It sounds like you're after the equivalent of "const" from C++. This doesn't exist in C#. There's no way of indicating that consumers can't modify the properties of an object, but something else can (assuming the mutating members are public, of course).

You could return a clone of the Foo as suggested, or possibly a view onto the Foo, as ReadOnlyCollection does for collections. Of course if you could make Foo an immutable type, that would make life simpler...

Note that there's a big difference between making the field readonly and making the object itself immutable.

Currently, the type itself could change things in both ways. It could do:

_Foo = new Foo(...);

or

_Foo.SomeProperty = newValue;

If it only needs to be able to do the second, the field could be readonly but you still have the problem of people fetching the property being able to mutate the object. If it only needs to do the first, and actually Foo is either already immutable or could be made immutable, you can just provide a property which only has the "getter" and you'll be fine.

It's very important that you understand the difference between changing the value of the field (to make it refer to a different instance) and changing the contents of the object that the field refers to.

这样的小城市 2024-07-23 05:40:17

不幸的是,目前 C# 中没有简单的方法来解决这个问题。 您可以在接口中提取 Foo 的“只读部分”,并让您的属性返回该部分而不是 Foo

Unfortunately, there's no easy way around this in C# at the moment. You could extract the "read only part" of Foo in an interface and let your property return that instead of Foo.

深海少女心 2024-07-23 05:40:17

正如您已经推测的那样,制作副本、ReadOnlyCollection 或使 Foo 保持不变通常是三种最佳途径。

每当我做比简单返回底层字段更重要的事情时,我有时会喜欢方法而不是属性,具体取决于涉及的工作量。 方法暗示着某些事情正在发生,并在消费者使用您的 API 时向他们发出更多信号。

Making a copy, a ReadOnlyCollection, or having Foo be immutable are usually the three best routes, as you've already speculated.

I sometimes favor methods instead of properties whenever I'm doing anything more significant than simply returning the underlying field, depending on how much work is involved. Methods imply that something is going on and raise more of a flag for consumers when they're using your API.

对不⑦ 2024-07-23 05:40:17

“克隆”您收到并返回的 Foo 对象是一种称为防御性复制的正常做法。 除非克隆有一些用户可见的看不见的副作用,否则绝对没有理由不这样做。 它通常是保护类的内部私有数据的唯一方法,特别是在 C# 或 Java 中,其中 C++ 的 const 思想不可用。 (即,为了用这两种语言正确创建真正不可变的对象,必须这样做。)

为了澄清,可能的副作用是用户(合理地)期望返回原始对象,或者保留某些资源由 Foo 提供,将无法正确克隆。 (在这种情况下,实现 IClonable 是做什么的?!)

"Cloning" the Foo objects you receive and give back out is a normal practice called defensive copying. Unless there is some unseen side-effect to cloning that will be visible to the user, there is absolutely no reason to NOT do this. It is often the only way to protect your classes' internal private data, especially in C# or Java, where the C++ idea of const is not available. (IE, it must be done in order to properly create truly immutable objects in these two languages.)

Just to clarify, possible side effects would be things like your user (reasonably) expecting that the original object be returned, or some resource being held by Foo that will not be cloned correctly. (In which case, what is it doing implementing IClonable?!)

天涯沦落人 2024-07-23 05:40:17

如果您不想让任何人扰乱您的状态...请不要暴露它! 正如其他人所说,如果某些东西需要查看您的内部状态,请提供它的不可变表示。 或者,让客户告诉你做某事(Google 的意思是“告诉不要问”),而不是自己做。

If you don't want anyone to mess with your state...don't expose it! As others have said, if something needs to view your internal state, provide an immutable representation of it. Alternatively, get clients to tell you to do something (Google for "tell don't ask"), instead of doing it themselves.

旧梦荧光笔 2024-07-23 05:40:17

为了澄清 Jon Skeet 的评论,您可以创建一个视图,这是可变 Foo 的不可变包装类。 这是一个例子:

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}

To clarify Jon Skeet's comment you can make a view, that is an immutable wrapper class for the mutable Foo. Here's an example:

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}
寒冷纷飞旳雪 2024-07-23 05:40:17

实际上,您可以在 C# 中重现 C++ const 的行为 - 您只需手动执行即可。

无论 Foo 是什么,调用者修改其状态的唯一方法是调用其方法或设置属性。

例如, Foo 的类型为 FooClass

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

因此,在您的情况下,您很高兴他们获得 Message 属性,但未设置它,你肯定不高兴他们调用这个方法。

因此,定义一个接口来描述它们可以执行的操作:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

我们省略了变异方法,只保留了属性的 getter 方法。

然后将该接口添加到 FooClass 的基本列表中。

现在,在具有 Foo 属性的类中,您有一个字段:

private FooClass _foo;

和一个属性 getter:

public IFooConst Foo
{
    get { return _foo; }
}

这基本上精确地手动重现了 C++ const 关键字将自动执行的操作。 在伪 C++ 术语中,const Foo & 类型的引用就像自动生成的类型,仅包含 Foo 中标记为 const< 的成员/代码> 成员。 将其翻译成理论上的 C# 未来版本,您可以像这样声明 FooClass

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

实际上,我所做的就是将 IFooConst 中的信息合并回 FooClass,通过使用新的 const 关键字标记一个安全成员。 因此,在某种程度上,除了这种模式的正式方法之外,添加 const 关键字不会给语言带来太多好处。

那么,如果您有一个对 FooClass 对象的 const 引用:

const FooClass f = GetMeAFooClass();

您将只能调用 f 上的 const 成员。

请注意,如果 FooClass 定义是公共的,调用者可以将 IFooConst 转换为 FooClass。 但他们也可以在 C++ 中做到这一点 - 这称为“抛弃 const”,并涉及一个名为 const_cast(const T &) 的特殊运算符。

还有一个问题是,接口在产品版本之间不太容易演变。 如果第三方可能实现您定义的接口(如果他们可以看到它,他们可以自由地执行),那么您无法在未来的版本中向其中添加新方法,而不要求其他人重新编译其代码。 但是,如果您正在编写一个可扩展的库供其他人构建,那么这只是一个问题。 也许内置的 const 功能可以解决这个问题。

You can actually reproduce the behaviour of C++ const in C# - you just have to do it manually.

Whatever Foo is, the only way the caller can modify its state is by calling methods on it or setting properties.

For example, Foo is of type FooClass:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

So in your case, you're happy for them to get the Message property, but not set it, and you're definitely not happy about them calling that method.

So define an interface describing what they're allowed to do:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

We've left out the mutating method and only left in the getter on the property.

Then add that interface to the base list of FooClass.

Now in your class with the Foo property, you have a field:

private FooClass _foo;

And a property getter:

public IFooConst Foo
{
    get { return _foo; }
}

This basically reproduces by hand precisely what the C++ const keyword would do automatically. In psuedo-C++ terms, a reference of type const Foo & is like an automatically generated type that only includes those members of Foo that were marked as const members. Translating this into some theoretical future version of C#, you'd declare FooClass like this:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

Really all I've done is merged the information in IFooConst back into FooClass, by tagging the one safe member with a new const keyword. So in a way, adding a const keyword wouldn't add much to the language besides a formal approach to this pattern.

Then if you had a const reference to a FooClass object:

const FooClass f = GetMeAFooClass();

You would only be able to call the const members on f.

Note that if the FooClass definition is public, the caller could cast an IFooConst into a FooClass. But they can do that in C++ too - it's called "casting away const" and involves a special operator called const_cast<T>(const T &).

There's also the issue of interfaces not being very easy to evolve between versions of your product. If a third party may implement an interface you define (which they are free to do if they can see it), then you can't add new methods to it in future versions without requiring others to recompile their code. But that's only a problem if you are writing an extensible library for others to build on. Maybe a built-in const feature would solve this problem.

清浅ˋ旧时光 2024-07-23 05:40:17

我正在考虑类似的安全问题。 或许有办法。 很清楚但不短。 总体思路非常简单。 然而我总是找到一些解决方法,所以从未测试过。 但你可以检查一下——也许它对你有用。

这是伪代码,但我希望其背后的想法很清楚,

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

因此在创建此属性的类中,您可以访问 SetMe 方法,但它仍然是私有的,因此除了创建者 Foo 之外,它看起来是不可变的。

尽管如此,对于任何大于几个属性的东西,这可能很快就会变得超级混乱 - 这就是为什么我总是更喜欢其他封装方式。 但是,如果不允许客户端更改 Foo 对您来说非常重要,那么这是一种选择。

然而,正如我所说,这只是理论。

I was thinking about similar security things. There is probably a way. Quite clear but not short. The general idea is quite simple. However I always found some ways around so never tested it. But you could check it - maybe it will work for you.

This is pseudo code, but I hope idea behind it is clear

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

So in class which creates this property you have access to SetMe method, but it's still private so except for creator Foo looks unmutable.

Still for anything bigger than few properties this will probably became soon super mess - that's why I always preferred other ways of encapsulation. However if it's super important for you to not allow the client to change Foo, than this is one alternative.

However, as I said, this is only theory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文