C# 在编译时对表达式求值

C# 在编译时对表达式求值,c#,visual-studio,compiler-construction,C#,Visual Studio,Compiler Construction,我知道这已经被问了很多,但只针对C/C++和Java。 问题与使用常量表达式的性能优势有关: 当我调用一个只有常量作为参数的静态函数时,有没有办法告诉编译器应该在编译时计算调用并用结果替换调用 例如: const double pi = Math.PI; //works as Math.PI is a constant const double spi = Math.Sin(Math.PI); //compiler error, because expression must be cons

我知道这已经被问了很多,但只针对C/C++和Java。 问题与使用常量表达式的性能优势有关:

当我调用一个只有常量作为参数的静态函数时,有没有办法告诉编译器应该在编译时计算调用并用结果替换调用

例如:

const double pi = Math.PI; //works as Math.PI is a constant  
const double spi = Math.Sin(Math.PI); //compiler error, because expression must be constant  
是否没有指令(更好的:Attributes)明确地告诉编译器Math.Sin()这样的静态方法不会在内部修改或读取任何数据,因此从技术上讲可以在编译时评估调用

哦,请不要回答“justdo
constdouble-spi=0
”,因为我的示例只是我遇到的问题的简化版本:在保持最高性能的同时提高代码的可维护性

谢谢你的帮助,非常感谢

没有副作用的方法有一个属性。但是,这仅用于代码分析,而不是编译器(目前)。然而,这在未来可能会改变

JetBrains ReSharper为相同的目的(代码分析)提供了类似的属性

因此,就目前而言,您需要一个类似于您预先计算的值的变通方法,最好是通过注释让其他人知道该值的来源:

const double spi = 0.0; // Math.Sin(Math.PI)


这当然会计算运行时的值,这是您不想要的。

对于数值常量,我看到两个选项:

选项一:使用静态只读(启动时计算一次):

选项二:使用袖珍计算器进行计算,并对这些常量进行硬编码:

class MyCalc
{
    // Math.Sin(Math.Pi)
    private const double spi = 0;
    // Math.Pi
    private const double pi = 3.141592653589793;

    public void Execute()
    {
        // .. whatever
    }
}
我不确定编译器是否可以在计算中完全优化选项1,但它应该是最可读和可维护的方式

如果您希望在编译时尽可能多地执行操作,那么事情就会变得更加困难。C++下有模板。我觉得他们写起来很麻烦,但人们已经写完了。这似乎变得容易了,但我还没有尝试过。我有一个非常强大的。但D是一个利基,我会避免在其中编写任何严肃的代码。我不知道其他语言有相当明确的预编译评估,但我肯定有一些

现在的编译器相当聪明。编译器很可能会看到在没有提示的情况下内联优化函数调用的机会。在Dotnet4.5中,我们有了-attribute,因此我们可能能够强制编译器进入正确的方向。C/C++也有类似的东西,而且曾经有过。我方的一般建议是,在您确切知道自己在做什么之前,不要使用
inline

<>如果你真的不想从C语言中走这条路,我的最佳选择是用上面提到的特性在C++中编写你的功能,编写一个易于使用的C接口并调用它。但是,先帮自己一个忙,衡量一下它是否真的值得。永远不要忘记优化的两条规则:

  • 不要
  • 暂时不要(仅限专家)

  • 无法将方法指定为纯函数。编译器无法确定Math.Sin()是否没有副作用。据我所知,没有。您可以做的是生成生成的代码(使用T4或自定义工具),或使用IL编织工具对生成的程序集进行后处理,或使用Roslyn创建自定义编译器。如果C#是口齿不清的话,这就不会那么难了你是想解决实际问题还是在这里需要一个与C++相关的特定语法?在C#const中,const的意思是const(编译后永不改变)。如果您想要一个不变的结果,还有其他方法可以实现,例如
    静态只读值,或者返回constant@Reinski:您的用例让我想起了Google。您给出了一个关于数值编译时常量的示例,您正在寻找一种有效的序列化/反序列化方法,现在正在研究一种独立于语言的协议定义,您抱怨没有帮助的答案?优化规则+1A+1用于选项“静态只读字段”,这是性能方面的最佳解决方案。A+1用于对纯属性的引用,这可能在将来使用。。。
    
    class MyCalc
    {
        private static readonly double spi = Math.Sin(Math.PI);
        private static readonly double pi = Math.PI;
    
        public void Execute()
        {
            // .. whatever
        }
    }
    
    class MyCalc
    {
        // Math.Sin(Math.Pi)
        private const double spi = 0;
        // Math.Pi
        private const double pi = 3.141592653589793;
    
        public void Execute()
        {
            // .. whatever
        }
    }