【问题标题】:When will a property get call create a local copy of a reference type? How to avoid that?何时调用属性会创建引用类型的本地副本?如何避免这种情况?
【发布时间】:2011-04-01 00:36:02
【问题描述】:

fooBase 类中有SomeProperty。在barBase 类中,我有一个fooBase 类型的字段:

public class fooBase 
{
    public object SomeProperty { get; set; }
}

public class barBase
{
    protected fooBase _foo;
}

很明显,我可以从 barBase 将 SomeProperty 更改为 _foo.SomeProperty = whatever;

现在在派生类fooChild 中有一些不重要的逻辑,派生类barChild 在其上运行。 barChild 构造函数获取 fooChild 的实例并存储它。

public class fooChild : fooBase { /* someLogic */ }

public class barChild : barBase
{
    public barChild(fooChild foo)
    {
        _foo = foo; // foo of [fooChild] type stored in _foo of [fooBase] type.
    }

    protected fooChild _getFoo // cast via 'as' to access fooChild logic
    { get { return _foo as fooChild; } }
}

现在要使用 fooChild 的逻辑,我需要访问 _foo as fooChild_getFoo 可以)。

问题get { return ... as ... } 是否会创建一个_foo 的本地副本,这样当我在barChild 的派生类中调用SomeFunction() 时,不会在barBase 中发生属性更改SomeProperty。 _foo?

public class somewhereElse : barChild
{
    public void SomeFunction()
    {
        _getFoo.SomeProperty = new object();
        // now barBase:_foo.SomeProperty is still old object?
    }
}

如果是,我该如何避免这种情况?
如果不是,我怎么知道?

【问题讨论】:

  • 我讨厌 foos 和 bar,如果你输入一些现实生活中的名字会更容易理解你在问什么,因为没有人愿意花更多的时间来理解你的问题。
  • @Akash Kava:我很抱歉我的 foos 和 bar 冒犯了你。复制/粘贴我的实际代码并将其去除所有不必要的部分需要做更多的工作。 @all:感谢您启发了我在 C++ 中的思维迷雾。 ^^

标签: c# properties pass-by-reference


【解决方案1】:

引用类型被复制的唯一时间是当您调用主动复制的方法或属性时。 Clone() 是一个明显的例子(除了一些例外,对于像字符串这样的不可变类型,Clone() 只返回同一个对象并假装它是一个新对象是安全的,因为什么也不能改变)。

as 始终是一个身份转换。 x = obj as T 相当于:

if(obj is T)
  x = (T)(object)obj;
else
  x = null;

我在那里有(T)(object) 而不仅仅是(T) 的原因是有时会存在一个隐式转换运算符用于在两种类型之间进行转换,并且可能创建一个新对象。与object 之间的转换不会(除非有人对转换运算符很愚蠢 - 我很确定如果他们那么愚蠢,编译器会阻止他们,但这是一个愚蠢的案例,我懒得测试,只是不要在对象之间进行类型转换)。

如果您想测试一个对象是否与另一个对象相同,请使用ReferenceEquals,这仅比较身份,即使 Equals 被覆盖,或者参数是装箱的值类型。

【讨论】:

    【解决方案2】:

    “as”关键字从不创建新对象(为什么要这样做?),所以 SomeProperty 是新对象

    (MySubClass)myInstance
    
    myInstance as MySubClass
    

    这几乎是一样的(而且都不会创建新实例)
    (如果 myInstance 不是 MySubclass 的类型则不一样,在这种情况下,第一行将抛出异常,而在第二行“as 运算符”将返回 null)

    【讨论】:

    • 所以虽然 _getFoo 只实现了 'get',但底层字段仍然可以修改?
    • 只要你返回的是类而不是 struct (valuetype) 那么是的 --- get acctualy 所做的是生成方法 fooChild _getFoo_get() 所以它将返回对 fooChild 的引用,你可以做任何事情你想要它(更改属性等)---但没有设置器你不能更改 barChild 中的 _foo 字段
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多