【问题标题】:Properties and ref属性和参考
【发布时间】:2021-04-12 07:47:32
【问题描述】:

考虑将类 A 与任何类型的私有字段 _data(例如 int)和属性 Data 一起使用该字段:

public class A
{
    private int _data;

    public int Data
    {
        get => _data;
        set => _data = value;
    }

    // Constructor is redudant, I created that for testing purposes...
    public A(int data)
    {
        _data = data;
    }
}

现在考虑类B 具有相同的私有字段_data 和属性Data,它只返回对我们字段的引用:

public class B
{
    private int _data;

    public ref int Data
    {
        get => ref _data;
    }

    // Constructor is redudant, I created that for testing purposes...
    public B(int data)
    {
        _data = data;
    }
}

现在我找不到答案的问题:如果Data 没有set 修饰符,为什么我能够在B 类的实例中更改_data 的值?

B b = new B(50);

// This line doesn't produce any warnings or errors
b.Data = 100;

Console.WriteLine(b.Data == 100); // True

它是否像 C/C++ 中的 pointers 一样工作,并且编译器理解这个属性只是一个 pointer,所以它会自动为这个指针指向的内容赋值(没有任何特殊的运算符和/或铸造)还是我遗漏了什么?

我在docs.microsoft.com(同时搜索Propertiesref)上找不到有关此问题的答案,因此不胜感激。

【问题讨论】:

  • 你可以做任何你想做的事情*_data。您不需要带有 setter 的属性来修改同一类中的私有字段。
  • _data 不知道数据。它们都是完全独立的整数。一个属性将用于控制对类外部私有字段的访问,但在内部你可以对 _data 做任何事情(就像@BrootsWaymb 说的那样)。

标签: c# properties ref


【解决方案1】:

确实,使用 ref 我们得到一个引用,我们可以更改值。

考虑这段代码:

A a = new A();
B b = new B();

a.Data = 100;
b.Data = 200;

IL生成的代码是:

// A a = new A();
IL_0001: newobj instance void ConsoleApp.A::.ctor()
IL_0006: stloc.0

// B b = new B();
IL_0007: newobj instance void ConsoleApp.B::.ctor()
IL_000c: stloc.1

// a.Data = 100;
IL_000d: ldloc.0
IL_000e: ldc.i4.s 100
IL_0010: callvirt instance void ConsoleApp.A::set_Data(int32)

// b.Data = 200;
IL_0016: ldloc.1
IL_0017: callvirt instance int32& ConsoleApp.B::get_Data()
IL_001c: ldc.i4 200
IL_0021: stind.i4
  • 使用setter,我们调用一个方法,并将int32 value作为参数传递。

  • 使用 by ref getter,我们得到了一个整数的引用:int32&

编译器说,通过 ref 获得 getter 的属性不能有 setter。

因为它是无用的和多余的。

因此,只能在by ref getter中进行处理,例如根据某些条件返回一个或另一个引用:

Enabled 的控件的CurrentColor 将返回ColorEnabled,否则返回ColorDisabled


a.Data = 100 转录的本机 x86 机器代码是:

mov         rcx,qword ptr [rbp+40h]  
mov         edx,64h  
cmp         dword ptr [rcx],ecx  
call        00007FF7E93B0568  

对于b.Data = 200 是:

mov         rcx,qword ptr [rbp+38h]  
cmp         dword ptr [rcx],ecx  
call        00007FF7E93B0580  
mov         qword ptr [rbp+20h],rax  
mov         rax,qword ptr [rbp+20h]  
mov         dword ptr [rax],0C8h  

【讨论】:

  • 我要补充一点,另一个重要的事情是stdind.i4的使用,这意味着store indirect i4,对于C程序员来说意味着*ptr = 200
  • 确实很好解释,谢谢! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2011-07-13
  • 1970-01-01
  • 2011-10-23
  • 2014-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多