c# - 在编译时强制进行狭窄的隐式强制转换

标签 c# compile-time coercion

我正在尝试定义一个结构,该结构使用具有有限范围数字的变量和来自整数的隐式强制转换。如果此结构使用任何常量或其他硬编码值,我希望能够强制生成错误。

这是我正在尝试完成的示例。

    byte a = 123; // Allowed
    byte b = 123123; // Not allowed
    const int x = 123;
    const int y = 123123;
    byte c = x; // Allowed
    byte d = y; // Not allowed

理想情况下,我希望能够,例如,将数字限制在 1 到 99 之间,以便 MyStruct s = 50;有效,但 MyStruct s = 150;导致像上面的字节 b 和 d 那样的编译时错误。

我找到了 something similar for a different language ,但不适用于 C#。

最佳答案

我认为您可以通过使用自定义属性和 roslyn 代码分析来做到这一点。让我勾勒出一个解决方案。这至少应该解决第一个用文字初始化的用例。

首先,您需要一个适用于您的结构的自定义属性,以允许代码分析能够知道有效范围:

[AttributeUsage(System.AttributeTargets.Struct)]
public class MinMaxSizeAttribute : Attribute
{
    public int MinVal { get; set;}
    public int MaxVal { get; set;}
    public MinMaxSizeAttribute()
    {
    }
}

您在这里所做的是将最小值和最大值存储在属性中。这样您就可以稍后在源代码分析中使用它。

现在将此属性应用于结构声明:

[MinMaxSize(MinVal = 0, MaxVal = 100)]
public struct Foo
{
    //members and implicit conversion operators go here
}

现在 Foo 结构的类型信息包含值范围。接下来您需要一个 DiagnosticAnalyzer 来分析您的代码。

public class MyAnalyzer : DiagnosticAnalyzer
{
    internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor("CS00042", 
        "Value not allowed here",
        @"Type {0} does not allow Values in this range", 
        "type checker", 
        DiagnosticSeverity.Error,
        isEnabledByDefault: true, description: "Value to big");
    public MyAnalyzer()
    {
    }

    #region implemented abstract members of DiagnosticAnalyzer

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeSyntaxTree, SyntaxKind.SimpleAssignmentExpression);
    }

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

    #endregion

    private static void AnalyzeSyntaxTree(SyntaxNodeAnalysisContext context)
    {

    }
}

这是参与代码分析的基本框架。分析器注册分析赋值:

context.RegisterSyntaxNodeAction(AnalyzeSyntaxTree, SyntaxKind.SimpleAssignmentExpression);

对于变量声明,您需要注册不同的 SyntaxKind,但为简单起见,我将在此处坚持使用一个。

让我们看一下分析逻辑:

private static void AnalyzeSyntaxTree(SyntaxNodeAnalysisContext context)
        {
            if (context.Node.IsKind(SyntaxKind.SimpleAssignmentExpression))
            {
                var assign = (AssignmentExpressionSyntax)context.Node;
                var leftType = context.SemanticModel.GetTypeInfo(assign.Left).GetType();
                var attr = leftType.GetCustomAttributes(typeof(MinMaxSizeAttribute), false).OfType<MinMaxSizeAttribute>().FirstOrDefault();
                if (attr != null && assign.Right.IsKind(SyntaxKind.NumericLiteralExpression))
                {
                    var numLitteral = (LiteralExpressionSyntax)assign.Right;
                    var t = numLitteral.Token;
                    if (t.Value.GetType().Equals(typeof(int)))
                    {
                        var intVal = (int)t.Value;
                        if (intVal > attr.MaxVal || intVal < attr.MaxVal)
                        {
                            Diagnostic.Create(Rule, assign.GetLocation(), leftType.Name);
                        }
                    }
                }
            }
        }

分析器所做的是,检查左侧的类型是否有与之关联的 MinMaxSize,如果有,它会检查右侧是否为文字。当它是文字时,它会尝试获取整数值并将其与与该类型关联的 MinValMaxVal 进行比较。如果值超出该范围,它将报告诊断错误。

请注意,所有这些代码大部分都未经测试。它编译并通过了一些基本测试。但它只是为了说明一个可能的解决方案。有关更多信息,请查看 Rsolyn Docs

您要涵盖的第二种情况更复杂,因为您需要申请 dataflow analyzes获取 x 的值。

关于c# - 在编译时强制进行狭窄的隐式强制转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32873098/

相关文章:

c# - LINQ:模拟 sql 的 EXISTS?

c++ - 是否可以在静态 vector_c 上使用 boost::mpl::contains?

javascript - 为什么字符串对后增量 (something++) 和加 1 (something = something +1) 产生不同的输出?

java - 为什么 ArrayIndexOutOfBoundsException 不是编译时错误?

javascript - JavaScript 如何处理++ 运算符?

r - 如何将字符类中的阿拉伯数字转换为 R 中的英文数字?

c# - 按钮单击打开 richTextBox 并显示读取文件

c# - 强制.Net程序使用 '.'作为十进制符号

c# - 实现接口(interface)的实体的通用配置

c++ - 常量的编译时检查