13

我刚刚遇到了 C# 5 调用者信息属性 ( http://msdn.microsoft.com/en-us/library/hh534540.aspx )。

这似乎是一个非常有用的功能,我已经阅读了一些文档(http://www.codeproject.com/Tips/606379/Caller-Info-Attributes-in-Csharp)。

但是,我只是想知道:为什么必须传入默认值?它们是如何使用的?

以下示例代码显示了如何使用调用者信息属性:

public static void ShowCallerInfo([CallerMemberName] 
  string callerName = null, [CallerFilePath] string 
  callerFilePath = null, [CallerLineNumber] int callerLine=-1)
{
    Console.WriteLine("Caller Name: {0}", callerName);
    Console.WriteLine("Caller FilePath: {0}", callerFilePath);
    Console.WriteLine("Caller Line number: {0}", callerLine);
}

我的问题是: 、 和 的默认值nullnull什么-1?上面的代码与以下代码有何不同:

public static void ShowCallerInfo([CallerMemberName] 
  string callerName = "hello", [CallerFilePath] string 
  callerFilePath = "world", [CallerLineNumber] int callerLine=-42)
{
    Console.WriteLine("Caller Name: {0}", callerName);
    Console.WriteLine("Caller FilePath: {0}", callerFilePath);
    Console.WriteLine("Caller Line number: {0}", callerLine);
}

按照我的理解,这些是可选参数,编译器提供默认值,替换我们分配的任何默认值。在那种情况下,我们为什么要指定默认值?是否存在一些奇怪的边缘情况,编译器可能无法填写值,并诉诸我们提供的默认值?如果不是,那么为什么要求我们输入这些数据?要求开发人员提供永远不会使用的默认值似乎相当笨拙。

免责声明:我尝试用谷歌搜索,但我找不到任何东西。我几乎不敢问关于 SO 的问题,因为大多数这样的新手问题都会遇到这样的敌意,但作为最后的手段,我会冒险提出一个问题。版主/高级用户,无意冒犯 - 在发布此之前,我确实尝试过在其他地方查找信息。

4

4 回答 4

8

这些参数需要一个默认值,因为调用者信息属性是使用可选参数实现的,而可选参数需要一个默认值。这样调用就可以简单ShowCallerInfo()而无需发送任何参数,编译器将添加相关参数。

为什么一开始就使用可选参数来实现它是一个更深层次的问题。他们本可以没有,编译器需要在实际编译开始之前“注入”这些参数,但与可选参数(这是一个C# 4.0特性)相反,它不会向后兼容,并且会破坏其他编译器/代码分析工具

于 2014-06-23T10:40:46.643 回答
4

他们需要默认值,以便可以将参数标记为可选。如果你在调用方法时没有指定参数,编译器会为你注入正确的值,但前提是你没有指定它们。如果你这样做了,那么这些属性的“魔法”就不会发生。

据我了解,这些属性不会影响运行时,纯粹用于编译时,因此默认值只是为了确保参数是可选的。

于 2014-06-23T03:30:21.247 回答
2

换句话说,在被调用者(将属性应用于参数的地方调用的方法)上,参数必须存在。另一方面,调用者必须传递这些参数,编译器允许未指定参数的唯一方法是给它一个默认值。

尽管属性可能会影响代码生成或运行时执行,但如果删除所有属性,则源必须是有效的。因此,必须在被调用者上定义默认值,编译器仅根据应用的属性生成参数值,而不是在被调用者上定义的当前默认值。

于 2014-06-23T15:09:53.973 回答
1

其他答案中提到了一些似乎都有效的用途。

他们错过的是这些本质上告诉编译器用静态值重写对这些函数的调用。但这些值并不总是可用的。在这些情况下,编译器不会重写调用,因此将使用默认值。

例子:

  1. 如果您使用具有这些属性的函数编译 dll,将其公开给内存中生成的脚本(例如通过 Roslyn),则该代码可能没有“文件名”。

    • 有人可能会争辩说,生成的脚本应该使用提供的参数值来调用方法,但这意味着编译器可以静态编译的相同代码(即csc mycodefile.cs)在运行时无法与动态编译一起使用,即使使用相同的上下文也会令人困惑。
  2. 您还可以通过反射调用此方法,编译器根本不知道添加这些值。

    • 可以构建运行时/BCL 以强制反射调用者提供这些值,但无论如何,在该上下文中文件名和行号没有任何有意义的值。
  3. 您还可以添加[CallerMemberName]到属性构造函数并将该属性应用于类。这将没有成员名称。

查看文档中的成员名称

属性构造函数

应用属性的方法或属性的名称。如果属性是成员中的任何元素(例如参数、返回值或泛型类型参数),则此结果是与该元素关联的成员的名称。

无包含成员(例如,应用于类型的程序集级别或属性)

可选参数的默认值。

如果要隐藏调用者信息,还可以显式提供值。由于某些原因。(可能如果您使用代码混淆,这些值可能不会受到影响,因此您可能希望在这些情况下提供这些值以隐藏调用者)。

见文档中的备注

调用方信息值在编译时作为文本发送到中间语言 (IL)。与 StackTrace 属性的异常结果不同,结果不受混淆的影响。

您可以显式提供可选参数来控制调用者信息或隐藏调用者信息。

于 2019-11-20T12:48:56.073 回答