2015-09-30 19 views
12

制限のある範囲の変数とintからの暗黙的な変換を使用する構造体を定義しようとしています。私は任意の定数または他のハードコードされた値がこの構造体で使用されている場合、ビルドエラーを強制することができるようにしたいと思います。コンパイル時に狭い暗黙の強制を強制する

ここに私が達成しようとしているものの例があります。

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 

Iは、理想的には、体mystruct S = 50のように、例えば、1から99までの数を制限できるようにしたいです。動作しますが、MyStructは150です。上記のバイトbとdのようなコンパイル時エラーが発生します。

something similar for a different languageが見つかりましたが、C#では見つかりませんでした。

+0

できません。バイトは255の範囲の型です。コンパイル時にこれを制限したり、カスタム型を作成することはできません。 @ –

+0

@M。kazemAkhgary Roslynを修正することで可能かもしれませんが、私はどれくらい難しくて妥当かは分かりません –

+0

興味深い質問! Visual Studio 2013で、大きすぎるリテラル値を入力すると、Intellisenseが認識します。似たようなIntellisenseサポートを持つクラスを定義する方法があるのだろうか、それとも焼き付けられているのだろうか。 –

答えて

0

私はあなたがカスタム属性とroslynコード分析を使ってこれを行うことができると思います。私は解決策をスケッチしよう。これは少なくともリテラルで初期化する最初のケースを解決するはずです。あなたはここで行うことはあなたが最小値と最大値を格納している

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

まず、コードが有効範囲を知ることができるように分析できるように、あなたの構造体に適用されるカスタム属性が必要になります属性。そうすれば、ソースコード分析の後半でこれを使うことができます。

は今のstruct宣言にこの属性を適用します。

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

今のstruct 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

xの値を取得するためにdataflow analyzesを適用する必要があるため、カバーする2番目のケースは複雑です。

関連する問題