【问题标题】:Overloading Property Setters重载属性设置器
【发布时间】:2015-04-10 16:25:52
【问题描述】:

我在看this question,这让我感到好奇。

每当我定义一个具有自动属性的类时,

// Example A
public class MyObject
{
  public int MyInt { get; set; }
}

JIT 编译器会将其转换为类似于以下内容:

// Example B
public class MyObject
{
  private int _MyInt;

  public int get_MyInt()
  { 
    return _MyInt;
  }

  public void set_MyInt(int value)
  {
    _MyInt = value;
  }
}

因此,您可以假设编写如下内容:

// Example C.1
public class MyObject 
{
  public int MyInt { get; set; }

  public void set_MyInt(string value)
  {
    MyInt = int.Parse(value);
  }
}

或者可能是这样的:

// Example C.2
public class MyObject 
{
  private int _myInt;
  public int MyInt 
  {
    get { return _myInt; }
    set 
    { 
      _myInt = value; 
    }
    set(string) 
    { 
      _myInt = int.Parse(value); 
    }
  }
}

并且在没有编译器错误的情况下存在此功能。

// Example D
public void DoSomething(string someIntegerAsAString)
{
  var myObject = new MyObject()
  {
    MyInt = someIntegerAsAString
  };
}

是什么阻止了编译器说出诸如 Example D 之类的代码,在这些代码中推断出所需的结果并且它可以正常工作并符合预期? 示例 B 中显示了该功能。

这是否违背了语言设计者设计语言的工作和行为方式?

【问题讨论】:

  • 在第一个示例中没有什么能阻止您。编译错误会阻止你。不支持重载的属性设置器。您不妨只使用一种方法。可能还有一些奇特的方式来滥用它,那么为什么要在很少使用的东西上浪费时间、精力和金钱呢?
  • 事实上,您可以使用动态绑定 hanselman.com/blog/… 进行方法重载,这对于 auto-IMPLEMENTED 属性是不可能的,但它有时会有所帮助

标签: c#


【解决方案1】:

您确实可以这样做...

public class MyObject 
{
  public int MyInt { get; set; }

  public void set_MyInt(string value)
  {
    MyInt = int.Parse(value);
  }
}

...尽管将字符串转换为 int 会带来明显的性能开销。

这不起作用:

public class MyObject 
{
  private int _myInt;
  public int MyInt 
  {
    get { return _myInt; }
    set 
    { 
      _myInt = value; 
    }
    set(string) 
    { 
      _myInt = int.Parse(value); 
    }
  }
}

...因为 C# 不支持 setter 重载。然而,你可以用implicit type conversion 实现类似的东西,但它必须在你自己的类型上实现。 Integer 是一种值类型,因此它本质上是密封的。

【讨论】:

  • 我意识到这一点。但我所说的 key 是编译器识别此功能。我想我想要更多信息(在高层次上)为什么编译器不支持这样的东西,从我的角度来看,存在功能的基本框架。
  • @Cameron:我意识到我参加聚会很晚了,但作为 Eric Lippert 博客的常客(他曾经从事 C# 编译器工作),我认为最可能的解释是为什么 C# 编译器会这样做不允许重载的设置器是这样的:它根本不被认为是一个有用或足够重要的语言特性。正如 Eric 一直指出的那样,实现一个特性是有代价的,所以除非一个特性不是非常需要,否则它不会存在。
【解决方案2】:

不是JIT 为属性生成get_*set_* 方法。

如果你反编译这段代码:

public int MyInt { get; set; }

你会得到这个 IL:

.property instance int32 MyInt()
{
    .get instance int32 C::get_MyInt()
    .set instance void C::set_MyInt(int32)
} // end of property C::MyInt

.method public hidebysig specialname instance int32 
        get_MyInt() cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       11 (0xb)
    .maxstack  1
    .locals init (int32 V_0)
    IL_0000:  ldarg.0
    IL_0001:  ldfld      int32 C::'<MyInt>k__BackingField'
    IL_0006:  stloc.0
    IL_0007:  br.s       IL_0009
    IL_0009:  ldloc.0
    IL_000a:  ret
} // end of method C::get_MyInt

.method public hidebysig specialname instance void 
        set_MyInt(int32 'value') cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.1
    IL_0002:  stfld      int32 C::'<MyInt>k__BackingField'
    IL_0007:  ret
} // end of method C::set_MyInt

如果您查看PropertyInfo,您会发现它有一个GetMethod 和一个SetMethod。 “方法”,而不是“方法”。

这与框架相关,尽管我不知道任何语言中属性具有多个 getter 或 setter。你知道这样的语言吗?

【讨论】:

  • LinqPad 显示了 IL 的简化视图。我认为 ildasm 的输出在这里会更有帮助。
  • 因为它会显示 .property 并且您在那里设置了 getter 和 setter。
  • @svick D 就是这样一种语言。一个 setter 有一个带有一个参数的方法。与方法一样,您可以拥有多个不同类型的方法。
【解决方案3】:

C# 设计者确实让我们能够实现隐式类型转换。但是,他们可能不希望它与原始类型一起使用以避免混淆。

想象一下这几行:

var myvar = 2;
myvar = "4";

任何看到以上行的人都会认为我们在谈论像 JavaScript 这样的动态语言。在 JavaScript 中,myvar 将是从第二行开始的字符串。如果 C# 支持您所描述的内容,它仍然是 int。一方面,我会觉得这很混乱。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多