2015-09-30 2 views
12

Я пытаюсь определить структуру, которая использует переменную с ограниченным диапазоном чисел и неявное принуждение от ints. Я хотел бы иметь возможность принудительно создавать ошибки сборки, если с этой структурой используются константы или другие жестко заданные значения.Принудительное узкое неявное принуждение во время компиляции

Вот пример того, что я пытаюсь выполнить.

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 с = 50; работает, но MyStruct s = 150; вызывает ошибку времени компиляции, такую ​​как байты b и d выше.

Я нашел something similar for a different language, но не для C#.

+0

его невозможно. byte - это тип с диапазоном 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() 
    { 
    } 
} 

Что вы делаете здесь вы храните минимальный и максимальное значение атрибут. Таким образом, вы можете использовать это позже в анализах исходного кода.

Теперь применим этот атрибут в объявлении структуры:

[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, связанный с ним, и если да, он проверяет, с правой стороны является буквальным. Когда он является литералом, он пытается получить целочисленное значение и сравнивает его с MinVal и MaxVal, связанными с типом. Если значения превышают этот диапазон, он сообщит об ошибке диагностики.

Обратите внимание, что весь этот код в основном не проверен. Он компилирует и передает некоторые базовые тесты. Но он предназначен только для иллюстрации возможного решения. Для получения дополнительной информации см. Rsolyn Docs

Второй корпус, который вы хотите использовать, более сложный, потому что вам нужно будет применить dataflow analyzes, чтобы получить значение x.

Смежные вопросы