【问题标题】:can a method parameter pass an object by reference but be read-only?方法参数可以通过引用传递对象但只读吗?
【发布时间】:2011-03-26 21:50:57
【问题描述】:

C#:你可以让方法参数通过引用传递对象但只读吗?

例如:

void MyMethod(int x, int y, read-only MyObject obj)

其中obj 是一个对象引用,但在方法期间不能修改此对象。

这可以在 C# 中实现吗?

【问题讨论】:

标签: c# readonly pass-by-reference


【解决方案1】:

没有。 C# 与 C++ const 没有直接的类似物(它自己的 const 有所不同)。一个常见的 C# 模式是传递一个不允许修改的接口,例如IEnumerable。您还可以创建不可变副本或包装器。

【讨论】:

  • 你能详细说明一下吗?我需要让 MyObject 实现 IEnumerable 接口吗?
  • IEnumerable 就是一个例子。想想IEnumerableList 之间的区别。您可以清楚地修改List,但如果您将其用作IEnumerable(它实现),您所能做的就是按顺序访问元素(MoveNextCurrent)。您可以使用类似的只读属性创建自己的接口。
  • +1,这个和我的回答一样,没看到。但我会把我的留在那里作为“长版”。 :)
【解决方案2】:

如果你传递的对象的类是你写的,那么你很幸运。

问问自己:如果 C# 有 const 功能,我希望通过 const 对我的类的引用来禁止对对象的哪些操作?

然后定义一个interface 排除被禁止的操作。

例如:

class MyClass 
{
    public string GetSomething() { ... }

    public void Clobber() { ... }

    public int Thing { get { ... } set { ... } }
}

对应的“const”接口可能是:

interface IConstMyClass 
{
    public string GetSomething() { ... }

    public int Thing { get { ... } }
}

现在修改类:

class MyClass : IConstMyClass
{

现在您可以使用IConstMyClass 来表示const MyClass

 void MyMethod(int x, int y, IConstMyClass obj)

注意:有些人会告诉你这还不够。如果MyMethod 转换回MyClass 会怎样?但忽略它们。最终,实现者可以使用反射来绕过类型系统的任何方面 - see this amusing example

阻止反射攻击的唯一选择是简单的方法:制作一个完全断开连接的克隆。

【讨论】:

  • 虽然另一个答案基本上是相同的,但我非常感谢您付出的额外努力以 CPP 程序员可以理解的方式很好地解释它。这个答案是一项杰出的工作,非常有帮助。
  • @shawn1874 - 谢谢,多好的反馈! :)
【解决方案3】:

这在 C# 中是不可能的。

您可以通过传入不可变对象来防止这种情况发生。

【讨论】:

  • 如何传递它与不变性不同,两者在对象的情况下是完全正交的。
【解决方案4】:

没有任何机制可以为您做到这一点。

要么传递一个一次性副本,要么在类本身中构建不可变性,即使是临时性的。

【讨论】:

  • 你的意思是像原始对象的克隆?
  • 是的,如果可行的话。如果你这样做了,那么改变就无关紧要了。
  • 让你的类实现 ICloneable 接口的开销是多少?
  • 我不会,也就是说,我会做类似的事情,但我不会实现 ICloneable。这是一份破损的合同。至于开销,这是另一种方法,根据类的编写方式,您可能必须更改其中的部分内容以适应克隆。不过,运行时开销应该为零。
【解决方案5】:

你为什么不复制那个对象,然后在你的obj 保持不变的情况下对那个副本做任何你想做的事情。

在此处查看如何克隆您的对象:Deep cloning objects

【讨论】:

  • 您可能不想制作副本的一个原因是您担心制作副本的性能成本。
  • 我更喜欢不会增加内存成本的解决方案。
  • 该建议的另一个问题是编写“接收”方法的编码人员可能会假设它们对对象造成的状态更改将是永久性的,而实际上它们将被丢弃。
  • @Craig - 在 CLR 上分配临时引用对象的成本非常小,比 C++ 堆分配更接近堆栈分配。 smellegantcode.wordpress.com/2009/03/01/…
  • @Daniel:它不会是整个对象的副本,而不仅仅是引用吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-20
  • 2016-05-30
  • 2011-05-11
  • 1970-01-01
  • 2020-09-08
相关资源
最近更新 更多