Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# “如何创建引用类型属性”;只读“;_C#_Properties_Encapsulation_Readonly - Fatal编程技术网

C# “如何创建引用类型属性”;只读“;

C# “如何创建引用类型属性”;只读“;,c#,properties,encapsulation,readonly,C#,Properties,Encapsulation,Readonly,我有一个类Bar,它有一个包含引用类型Foo的私有字段。我想在公共财产中公开Foo,但我不希望财产的消费者能够改变Foo。。。但是,它应该可以通过Bar在内部进行更改,即,我不能将字段设置为只读 所以我想说的是: private _Foo; public Foo { get { return readonly _Foo; } } …这当然是无效的。我可以返回Foo的克隆(假设它是IClonable),但这对消费者来说并

我有一个类
Bar
,它有一个包含引用类型
Foo
的私有字段。我想在公共财产中公开
Foo
,但我不希望财产的消费者能够改变
Foo
。。。但是,它应该可以通过
Bar
在内部进行更改,即,我不能将字段
设置为只读

所以我想说的是:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

…这当然是无效的。我可以返回
Foo
的克隆(假设它是
IClonable
),但这对消费者来说并不明显。我是否应该将属性的名称更改为
foopcopy
??它应该是
GetCopyOfFoo
方法吗?你认为最好的练习是什么?谢谢 您可以按照建议返回Foo的克隆,或者可能返回Foo上的视图,就像对集合一样。当然,如果你能使
Foo
成为一个不可变的类型,那会使生活更简单

请注意,将字段设置为只读与将对象本身设置为不可变之间有很大区别

目前,类型本身可以在两个方面进行更改。它可以做到:

_Foo = new Foo(...);

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


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

复制、
ReadOnlyCollection
或使
Foo
不可变通常是三条最佳途径,正如你已经推测的那样

当我做任何比简单返回基础字段更重要的事情时,我有时倾向于使用方法而不是属性,这取决于所涉及的工作量。方法意味着某些事情正在发生,当消费者使用您的API时,会给他们带来更大的影响。

克隆您接收和返回的Foo对象是一种称为“防御性复制”的正常做法。除非克隆有一些用户可以看到的看不见的副作用,否则绝对没有理由不这样做。它通常是保护类内部私有数据的唯一方法,尤其是在C++或java中,C++代码的“代码> const < /C>”是不可用的。(也就是说,必须这样做才能在这两种语言中正确地创建真正不可变的对象。)


只是想澄清一下,可能的副作用是您的用户(合理地)期望返回原始对象,或者Foo持有的某些资源无法正确克隆。(在这种情况下,它在实现IClonable做什么?!)

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

为了澄清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; }}
}

如果你不想让任何人扰乱你的国家…不要揭露它!正如其他人所说,如果需要查看您的内部状态,请提供它的不变表示。或者,让客户告诉你做某事(谷歌)“不问”,而不是自己做。

你实际上可以复制C++中的C++ const的行为,你只需要手动完成。 无论
Foo
是什么,调用方修改其状态的唯一方法是调用其上的方法或设置属性

例如,
Foo
的类型为
FooClass

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

    public string Message
    {
        get { ... }
        set { ... }
    }
}
因此,在您的情况下,您很高兴他们获得
Message
属性,但不设置它,而且您肯定不高兴他们调用该方法

因此,定义一个接口来描述他们可以做什么:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}
我们省略了mutating方法,只剩下属性的getter

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

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

private FooClass _foo;
和属性获取程序:

public IFooConst Foo
{
    get { return _foo; }
}
<>这基本上是手工复制的,C++的代码> const < /c>关键字会自动完成。在psuedo-C++术语中,类型为
const-Foo&
的引用类似于自动生成的类型,只包括标记为
const
成员的
Foo
的成员。将此转换为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 < /C>”,并涉及一个特殊的操作符,叫做“代码> conista C铸造(const t &))/c> 还有一个问题是
const FooClass f = GetMeAFooClass();
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
  }
}