为什么C#调用者信息属性需要默认值?

时间:2014-06-23 03:25:11

标签: c# c#-5.0 callermembername

我刚刚遇到了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 个答案:

答案 0 :(得分:8)

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

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:0)

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

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

示例:

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

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

    • 可以构建Runtime / BCL来强制反射调用程序提供这些值,但是在那种情况下,对于文件名和行号,没有任何有意义的值。
  3. 您还可以将[CallerMemberName]添加到属性构造函数并将该属性应用于类。这将没有成员名称。

  

请参阅文档中的Member Names

     

属性构造器

     

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

     

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

     

可选参数的默认值。

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

  

请参阅文档中的Remarks

     

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

     

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