2015-09-30 17 views
12

Ich versuche, eine Struktur zu definieren, die eine Variable mit einem eingeschränkten Bereich von Zahlen und implizite Nullen von Ints verwendet. Ich möchte Build-Fehler erzwingen können, wenn Konstanten oder andere hardcoded Werte mit dieser Struktur verwendet werden.Erzwinge eine enge implizite Koerzition zur Kompilierzeit

Hier ist ein Beispiel für das, was ich erreichen möchte.

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 

Ich würde im Idealfall zum Beispiel in der Lage sein möchten, eine Reihe von 1 bis 99 beschränken, so dass MyStruct s = 50; funktioniert aber MyStruct s = 150; verursacht einen Kompilierzeitfehler wie die obigen Bytes b und d.

Ich fand something similar for a different language, aber nicht für C#.

+0

ist nicht möglich. Byte ist ein Typ mit einem Bereich von 255. Ich denke nicht, dass Sie dies in der Kompilierzeit begrenzen oder benutzerdefinierten Typ erstellen können. –

+0

@M.kazemAkhgary Es könnte möglich sein, Roslyn zu modifizieren, obwohl ich mir nicht sicher bin, wie schwer oder vernünftig das wäre. –

+0

Interessante Frage! Wenn ich in Visual Studio 2013 einen Literalwert einlege, der zu groß ist, weiß der Intellisense. Ich frage mich, ob es eine Möglichkeit gibt, eine Klasse mit ähnlicher Intellisense-Unterstützung zu definieren, oder ob das gebacken ist. –

Antwort

0

Ich denke, Sie können dies tun, indem Sie benutzerdefinierte Attribute und Roslyn-Code-Analysen verwenden. Lassen Sie mich eine Lösung skizzieren. Dies sollte zumindest den ersten Anwendungsfall lösen, in dem Sie mit einem Literal initialisiert werden.

Zuerst würden Sie ein benutzerdefiniertes Attribut benötigen, die auf Ihre Struktur gilt der Code zu ermöglichen Analysen der Lage sein, den gültigen Bereich zu wissen:

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

Was Sie tun, ist hier speichern Sie die minimalen und maximalen Wert in ein Attribut Auf diese Weise können Sie dies später in den Quellcodeanalysen verwenden.

nun dieses Attribut auf die Strukturdeklaration gelten:

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

Nun ist die Typinformationen für die Struktur Foo enthält den Wertebereich. Das nächste, was Sie brauchen, ist eine DiagnosticAnalyzer, um Ihren Code zu analysieren.

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) 
    { 

    } 
} 

Dies ist das Skelett des bloßen Knochens, um an Codeanalysen teilzunehmen. Der Analysator registriert Zuordnungen analysieren:

context.RegisterSyntaxNodeAction(AnalyzeSyntaxTree, SyntaxKind.SimpleAssignmentExpression); 

Für Variablendeklarationen würden Sie für einen anderen SyntaxKind registrieren müssen, aber der Einfachheit halber werde ich ein hier bleiben.

Ermöglicht bei den Analysen Logik einen Blick:

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); 
         } 
        } 
       } 
      } 
     } 

Was der Analysator tut, ist, auf der linken Seite, wenn der Typ prüft ein MinMaxSize mit ihm verbunden hat und wenn ja, prüft sie, ob die rechte Seite ist ein Literal. Wenn es sich um ein Literal handelt, versucht es den ganzzahligen Wert zu erhalten und vergleicht es mit den MinVal und MaxVal, die dem Typ zugeordnet sind. Wenn die Werte diesen Bereich überschreiten, wird ein Diagnosefehler gemeldet.

Bitte beachten Sie, dass dieser Code größtenteils noch nicht getestet wurde. Es kompiliert und bestanden einige grundlegende Tests. Aber es soll nur eine mögliche Lösung veranschaulichen. Weitere Informationen finden Sie unter Rsolyn Docs

Der zweite Fall, den Sie abdecken möchten, ist komplexer, weil Sie dataflow analyzes anwenden müssen, um den Wert x zu erhalten.