【问题标题】:why Enum.ToString not acceptable as const string for Attribute parameter string type?为什么 Enum.ToString 不能作为 Attribute 参数字符串类型的 const 字符串?
【发布时间】:2016-12-03 12:52:22
【问题描述】:

[TL;DR] 为什么 const string SomeConstString = SomeEnum.OneEnumValue.ToString(); 在 C# 中是不允许的,即使它可以在编译时处理?

以下属性定义来自Azure WebJobs SDK;

[AttributeUsage(AttributeTargets.Parameter)]
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class ServiceBusTriggerAttribute : Attribute
{
    private readonly string _queueName;
    private readonly string _topicName;
    private readonly string _subscriptionName;

    /// <summary>
    /// Initializes a new instance of the <see cref="ServiceBusTriggerAttribute"/> class.
    /// </summary>
    /// <param name="queueName">The name of the queue to which to bind.</param>
    public ServiceBusTriggerAttribute(string queueName)
    {
        ...
    }

代码的用法如下;

public static void GetBapulActivityFromTopic(
        [ServiceBusTrigger("SomeConstString")] BrokeredMessage message,
        TextWriter log)
    {
        ...
    }

在上述用法中,我知道"SomeConstString" 应该是const string 类型,因为值必须是编译时常量。

但是为什么SomeEnum.OneEnumValue.ToString() 不能是编译时常量呢?

【问题讨论】:

    标签: c# string enums constants


    【解决方案1】:

    即使SomeEnum.OneEnumValue 是const,根据 C# 语言规范,对其调用方法也不是编译时常量表达式。以下是允许的部分列表:

    • 文字(包括 null 文字)。
    • 对const 类和结构类型成员的引用。
    • 对枚举类型成员的引用。
    • 对const参数或局部变量的引用
    • 带括号的子表达式,它们本身就是常量表达式。

    常量表达式中不允许方法调用。有关完整列表,请参阅第 N.19 节。

    如果您使用的是 C# 6,则可以改用 nameof operator,它会生成一个编译时常量:

    const string SomeConstString = nameof(SomeEnum.OneEnumValue);
    

    【讨论】:

      【解决方案2】:

      因为Enum.ToString() 只是一个常规的方法调用。它内部包含它自己的逻辑。例如, if 将检查给定的枚举是否具有[Flags] 属性,如果是 - 将以不同的方式将枚举值转换为字符串。 C#编译器不会在编译时调用任意方法,所以不能是编译时常量。

      无论如何,在这种情况下使用枚举并不是一个好主意。例如,天蓝色队列名称可以以数字开头并包含破折号(我记得),而枚举不能。也许今天你不使用这样的队列名称,但谁知道明天会发生什么。所以最好用公共常量定义静态类。

      【讨论】:

        【解决方案3】:

        不幸的是,C#(和 CLR/.NET)没有 C++ 的 constexpr 概念。因为 CLR 不支持它意味着编译器无法做出该假设,因此它失败了。

        但是 FWIW,CLR 确实不在编译时处理您的表达式,它将始终在运行时进行评估。

        在现代 C# 中,解决方法是使用 nameof 和 T4 等结构。

        更具体地说,SomeEnum.OneEnumValue 本身是一个字段访问器,.ToString() 是一个virtual 实例方法。假设另一个 CLR 环境可以合法地添加一些技巧,使 ToString() 方法根本不可预测(例如,考虑本地化/线程文化),因此它显然无法在编译时。

        另一个反论点:如果 SomeEnum 完全定义在一个可分离的程序集 (.dll) 文件中会怎样?程序集之间的链接仅在运行时发生,因此如果您要替换包含 SomeEnum 的程序集,则运行时不能假定 .ToString() 将返回相同的值。

        另外,请注意 static readonly 和 const 不等价:

        如果您有引用 AssemblyB 的 AssemblyA,并且 AssemblyA 使用在 AssemblyB 中定义的 const String,则即使在运行时替换了 AssemblyB,AssemblyA 实际上也会包含来自 AssemblyB 的 const 值的副本 .这是 CLR 中 const 本质的一部分。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-21
          • 2021-10-13
          • 1970-01-01
          • 2013-01-12
          相关资源
          最近更新 更多