【问题标题】:How do Properties actually work in C#, for example DateTime.Today? [duplicate]属性如何在 C# 中实际工作,例如 DateTime.Today? [复制]
【发布时间】:2015-06-28 01:35:51
【问题描述】:

我刚刚看到一些使用 struct DateTime.Today 的 sn-p 代码,但我无法理解它的内部工作原理。这是具体的行:

if (DateTime.Today.DayOfWeek == DayOfWeek.Monday)

使用Go To Definition F12后,看到Today静态方法返回 带有当前计算机中日期信息的 DateTime 对象。我想 Today 方法 可以用作构造函数包装器。我无法推断(更像是猜测)是如何在不先实例化 Today 结构的情况下访问 属性 DayOfWeek

谁能解释一下这怎么可能?我唯一的猜测是,当 VS 将代码编译为 IL 时,它可能会将这个语法糖转换为:

if ( (DateTime.Today()).DayOfWeek == DayOfWeek.Monday )

也许这就像水一样干净,但我是 C# 新手,所以我无法弄清楚。

提前致谢!

【问题讨论】:

标签: c# datetime methods properties dayofweek


【解决方案1】:

与您的问题相关,DateTime.TodayDateTimeProperty 类型,因此它具有DateTime.DayOfWeek Property 与任何常规DateTime 结构一样。

从广义上讲,Properties 如何在 C# 中工作:首先,为了更好地理解,您可能会将 Properties 视为伪装成一个字段的 Methods,因此您对 @987654332 的解释@ 作为一些假设的 GetToday() 方法在逻辑上是接近的,但不是“学术上精确的”(参见 @CalebB 的 cmets)。

我建议您熟悉 .NET Properties (https://msdn.microsoft.com/en-us/library/x9fsa0sw.aspx) 和 DateTime Structure (https://msdn.microsoft.com/en-us/libraRy/system.datetime.aspx)

希望这会有所帮助。

【讨论】:

  • 正如我在@toby 的回答中提到的,将属性视为方法并不完全正确,方法是委托的一种形式,而属性则不是。一个属性有一个 getter 和/或 setter,它们可能会或可能不会调用外部命名方法,如我对托比答案的评论中所示。
  • 确实,这种解释通常不是“学术上的精确”,并且作为任何类比它可能存在一些缺陷,但与此和许多其他案例相关,对于帮助初学者理解否则有点不清楚的问题。最好的问候,
  • @AlexBell 属性只是编译器为您生成的一种方法,如果您查看下面发布的汇编代码,您会发现基本上没有区别
【解决方案2】:

你说得对,欢迎来到properties的精彩世界。

DateTime.Today 是一个属性,简而言之是编译器生成的函数,它转换为DateTime.get_today()。 所以这个表达式实际上是

if ( (DateTime.get_today()).DayOfWeek == DayOfWeek.Monday )

例子:

public class Test
{
    private string _lastName = "LName";
    private string _firstName = "FName";
    public string Name { get{
        return _lastName + " " + _firstName;
    } }

    public string GetName()
    {
        return _lastName + " " + _firstName;
    }
}
class Program
{
    static void Main(string[] args)
    {
        var test = new Test();
        Console.WriteLine(test.Name);
        Console.WriteLine(DateTime.Today.DayOfWeek);
    }
}

反编译GetName

.method public hidebysig instance string 
        GetName() cil managed
{
  // Code size       28 (0x1c)
  .maxstack  3
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldfld      string ConsoleApplication1.Test::_lastName
  IL_0007:  ldstr      " "
  IL_000c:  ldarg.0
  IL_000d:  ldfld      string ConsoleApplication1.Test::_firstName
  IL_0012:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
  IL_0017:  stloc.0
  IL_0018:  br.s       IL_001a
  IL_001a:  ldloc.0
  IL_001b:  ret
} // end of method Test::GetName

反编译Name

.method public hidebysig specialname instance string 
        get_Name() cil managed
{
  // Code size       28 (0x1c)
  .maxstack  3
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldfld      string ConsoleApplication1.Test::_lastName
  IL_0007:  ldstr      " "
  IL_000c:  ldarg.0
  IL_000d:  ldfld      string ConsoleApplication1.Test::_firstName
  IL_0012:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
  IL_0017:  stloc.0
  IL_0018:  br.s       IL_001a
  IL_001a:  ldloc.0
  IL_001b:  ret
} // end of method Test::get_Name

反编译Main(方法调用)

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       56 (0x38)
  .maxstack  1
  .locals init ([0] class ConsoleApplication1.Test test,
           [1] valuetype [mscorlib]System.DateTime CS$0$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication1.Test::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance string ConsoleApplication1.Test::get_Name() //here
  IL_000d:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0012:  nop
  IL_0013:  ldloc.0
  IL_0014:  callvirt   instance string ConsoleApplication1.Test::GetName()//here
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001e:  nop
  IL_001f:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Today()//here
  IL_0024:  stloc.1
  IL_0025:  ldloca.s   CS$0$0000
  IL_0027:  call       instance valuetype [mscorlib]System.DayOfWeek [mscorlib]System.DateTime::get_DayOfWeek()
  IL_002c:  box        [mscorlib]System.DayOfWeek
  IL_0031:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0036:  nop
  IL_0037:  ret
} // end of method Program::Main

如您所见,NameGetName 之间绝对没有区别,除了 Name 是由编译器为您生成的 get_Name

更新 1

至于DateTime.Today,它实际上变成了

System.DateTime [mscorlib]System.DateTime::get_Today()

您必须了解的是,即使编译器为您生成了这些函数,也无法直接访问它们,因为它生成的是 IL 代码(.NET 的汇编代码)而不是 C#(随着 Roslyn C# 编译器,但不太了解)

如果您真的对应用程序中真正发生的事情感到好奇,我建议您使用ildasm.exe,它可以让您查看编译器生成的 IL。一本很好的书,它通过 C# 调用 CLR,我接触过第 3 版,但显然现在有第 4 版了。

【讨论】:

  • 我认为在这种情况下值得一提的是getter和setter,这种情况下的getter调用并返回GetToday方法的返回值,但它不必解析为调用另一个命名方法。示例:string FullName { get { return first + " " + last; } }
  • @CalebB 很抱歉,但我不明白你的意思。没有Today,就像没有FullName一样,这两个属性只是语法糖,更容易调用GetFullName()GetToday()方法。 Today 没有调用GetTodayTodayGetToday()
  • 您的描述有缺陷,今天是今天“获取”的属性,但不是定义为“GetToday()”的委托,即使它“似乎”具有同样的结果。一种看待它的方式是 GetToday 是一个被调用的委托对象,而 Today 是一个属性,其中 Today.GetValue() 被调用并从你的 get 语句中返回一个值。看看你的链接。
  • @downvoter 需要解释一下吗?
  • @CalebB 我重申,属性只是编译器为您生成的一个函数,仅此而已,我从未说过任何关于委托的事情,我只是说它是语法糖。答案唯一不正确的是它是 get_Today 而不是 GetToday()。贴出反汇编代码。
猜你喜欢
  • 2012-04-27
  • 1970-01-01
  • 2011-09-19
  • 1970-01-01
  • 2019-01-01
  • 2017-07-05
  • 1970-01-01
  • 2013-10-09
  • 2021-05-10
相关资源
最近更新 更多