为什么c#调用者信息属性需要一个默认值

本文关键字:一个 默认值 调用者 信息 属性 为什么 | 更新日期: 2023-09-27 18:09:44

我刚刚看到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);
}

我的问题是:null, null, -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的问题,因为大多数这样的新手问题都会遇到这样的敌意,但作为最后的手段,我要冒险提一个问题。版主/高级用户,无意冒犯-我真的试图在其他地方找到信息张贴这篇文章之前。

为什么c#调用者信息属性需要一个默认值

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

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

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

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

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

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

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

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

例子:

  1. 如果你编译一个带有这些属性的函数的dll,将其暴露给内存中生成的脚本,(例如通过Roslyn),该代码可能没有"文件名"。

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

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

参见Docs中的成员名

属性构造器

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

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

可选参数的默认值

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

参见文档注释

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

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