alexyorke/PurelySharp
GitHub: alexyorke/PurelySharp
这是一个基于 Roslyn 的 C# 代码分析器,旨在通过特定的特性标记来强制执行方法的函数式纯度,从而避免副作用。
Stars: 2 | Forks: 0
# PurelySharp Analyzer
一个旨在帮助在 C# 项目中强制执行方法纯度的 Roslyn 分析器。
**注意:** 本项目目前处于**非常早期的开发阶段**。下文描述的功能反映了_目前_的实现状态。最终目标是提供详细的纯度分析,但这**尚未实现**。
## 目标
PurelySharp 的主要目标是为开发者提供工具,以识别旨在成为函数式纯方法的方法,并标记与该意图相关的潜在问题。
## 当前状态与功能
目前,该分析器提供以下检查:
1. **`[EnforcePure]` / `[Pure]` 特性:** 使用 `PurelySharp.Attributes.EnforcePureAttribute` 或 `PurelySharp.Attributes.PureAttribute` 来标记旨在成为纯函数的方法。该分析器将 `[Pure]` 视为 `[EnforcePure]` 的可互换简写。同时应用这两者会产生 `PS0005`(冲突)。
2. **PS0002:未验证纯度:** 如果方法被标记了纯度特性(`[EnforcePure]` 或 `[Pure]`),并且引擎根据当前规则(见下文)无法确定它是纯的,它将报告诊断 `PS0002`。
3. **PS0003:特性位置不当:** 如果纯度特性(`[EnforcePure]` 或 `[Pure]`)被应用于方法_以外_的任何声明(例如,类、结构体、字段、属性、事件、参数),分析器将报告诊断 `PS0003`。
4. **PS0004:缺少特性:** 如果方法_没有_被标记纯度特性,但分析引擎根据当前实现的规则确定它_很可能_是纯的,它将报告诊断 `PS0004` 作为建议。
5. **基本纯度分析:** 有限的分析引擎(`PurityAnalysisEngine`)尝试验证方法的纯度。它检查:
- 已知的非纯操作(例如,一些 I/O、`DateTime.Now`、字段赋值)。
- 调用方法的纯度(带有循环检测的递归检查)。
- 表达式的纯度(常量、参数、`static readonly` 字段、基本运算符等)。
- 基本语句的纯度(局部声明、return、简单的表达式语句)。
**目前尚未详细实现(或计划中)的功能:**
- **对方法体的全面分析。** 虽然存在基本分析,但许多 C# 特性和控制流结构尚未处理(例如,`if`、循环、`try`、`using`、`async`/`await`、LINQ、复杂赋值、大多数框架方法)。
- 报告诊断的代码修复。
- 配置选项。
- 支持其他特性(如 `[AllowSynchronization]`)。
- 对构造函数、委托、属性、索引器等的详细分析。
## 工作原理
分析器(`PurelySharp.Analyzer`)与 C# 编译过程集成:
1. 它识别方法声明和其他符号。
2. 它检查纯度特性(`[EnforcePure]` 或 `[Pure]`)的存在和位置。
3. 如果任一纯度特性被放错位置(不在方法上),它会报告 `PS0003`。
4. 如果带有实现的纯方法上有纯度特性,它会调用 `PurityAnalysisEngine` 检查其方法体。
- 如果引擎确定该方法(根据当前规则)_不是_纯的,它会报告 `PS0002`。
5. 如果带有实现的方法上没有纯度特性,它会调用 `PurityAnalysisEngine`。
- 如果引擎确定该方法(根据当前规则)_是_纯的,它会报告 `PS0004`。
6. **内部分析(`PurityAnalysisEngine`)仍在开发中,并不涵盖所有 C# 特性或保证正确性。**
## 安装
_该分析器尚未发布。_ 一旦发布,安装步骤可能包括:
1. **特性包:** 添加对 `PurelySharp.Attributes` NuGet 包的引用。
# 示例命令(包尚不可用)
dotnet add package PurelySharp.Attributes --version
2. **分析器包/VSIX:**
- 将 `PurelySharp.Analyzer` NuGet 包添加到您的项目中,以进行构建时分析。
- 在 Visual Studio 中安装 `PurelySharp.Vsix` 扩展以获取实时反馈。
## 本地构建和安装 (VSIX + NuGet)
使用提供的脚本为 Visual Studio 生成 VSIX,并为 `PurelySharp.Attributes` 生成本地 NuGet 包。
1. 先决条件
- 安装了“Visual Studio extension development”工作负载 (VSSDK) 的 Visual Studio 2022
- .NET SDK 8.0+
2. 构建产物
# 从仓库根目录
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1
# 可选:Debug 构建
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -Configuration Debug
脚本在任何失败时都会停止,并打印产物写入的位置。典型位置:
- VSIX: `PurelySharp.Vsix\bin\Release\net472\PurelySharp.Vsix.vsix`
- NuGet: `PurelySharp.Attributes\bin\Release\PurelySharp.Attributes..nupkg`
3. 在 Visual Studio 中安装 VSIX
- 关闭 Visual Studio。
- 双击生成的 `.vsix` 并完成安装程序。
- 重新打开 Visual Studio。
4. 使用本地 NuGet 包(`PurelySharp.Attributes`)
- 在 Visual Studio 中:工具 → NuGet 包管理器 → 程序包管理器设置 → 程序包源
- 添加一个指向 `PurelySharp.Attributes\bin\Release` 的新本地源
- 在您的测试项目中:管理 NuGet 包 → 选择本地源 → 安装 `PurelySharp.Attributes`
5. 更新/卸载
- 重新运行构建脚本以生成新的 VSIX/NuGet;重新安装 VSIX 以进行更新
- 在 Visual Studio 中通过 扩展 → 管理扩展 来管理已安装的扩展
## 使用方法
1. 引用 `PurelySharp.Attributes` 项目(或包,一旦可用)。
2. 在您打算成为函数式纯的方法上添加 `[EnforcePure]` 或 `[Pure]`(来自 `PurelySharp.Attributes`)。
3. 错误地应用该特性将会看到 PS0003。
using PurelySharp.Attributes;
[Pure] // PS0003: Misplaced attribute on class
public class Calculator
{
[Pure]
public int Add(int a, int b)
{
// PS0002: Purity Not Verified will be reported currently,
// as no actual purity analysis is performed yet.
return a + b;
}
[Pure]
public int GetConstant(); // No implementation, PS0002 NOT reported.
[Pure] // PS0003: Misplaced attribute on field
private int _counter = 0;
}
4. 在构建期间或在 IDE 中观察 `PS0002` 和 `PS0003` 诊断(如果已安装 VSIX)。
注意:诊断信息可互换地指代 `[EnforcePure]` 和 `[Pure]`。
## 诊断
- **PS0002:未验证纯度**
- **消息:** `Method '{0}' is marked [EnforcePure]/[Pure], but its body contains operations the analyzer cannot prove pure`
- **严重性:** 警告
- **含义:** 该方法被标记为纯度分析,但必要的规则尚未实现。此诊断作为占位符。
- 注意:对标记有 `[EnforcePure]` 或 `[Pure]` 的方法触发。
- **PS0003:特性位置不当**
- **消息:** `The [EnforcePure] attribute can only be applied to method declarations.`
- **严重性:** 警告
- **含义:** 在不适用的声明类型(例如,类、结构体、字段、属性、参数)上发现了纯度特性(`[EnforcePure]` 或 `[Pure]`)。
- 注意:消息文本提到了 `[EnforcePure]`,但该规则同样适用于 `[Pure]`。
## 构建和测试
您可以使用 .NET CLI 构建解决方案并运行测试:
```
# 构建解决方案
dotnet build PurelySharp.sln
# 运行测试 (当前验证 PS0002/PS0003 行为)
dotnet test PurelySharp.sln
```
## 许可证
本项目基于 MIT 许可证授权。
## 支持的语言特性
支持意味着_已经_实现了_某种_程度的分析,但不保证 100% 的正确性或完整性。
`[x]` = 通常受支持/已处理。
`[/]` = 受到重大限制的部分支持/处理。
`[ ]` = 未显式处理或已知被视为非纯。
### 表达式
- [x] 字面量表达式(数字、字符串等)
- [x] `nameof` 和 `typeof` 表达式(编译时解析)
- [x] 标识符(局部变量、参数(`in`、`ref readonly`、值)、`static readonly` 字段)
- [/] 方法调用(递归检查、循环检测、小型的已知非纯列表。未对外部库进行深入分析。)
- [/] 成员访问(静态只读字段正常。实例字段、非只读静态字段、属性获取当前被视为非纯。)
- [ ] 对象创建(针对不可变类型) - 假定为非纯。
- [x] 元组表达式
- [ ] Switch 表达式 (C# 8.0+)
- [ ] 模式匹配
- [x] Null 合并运算符(`??`、`?.`)
- [ ] 插值字符串(除非是常量,否则假定为非纯。)
- [ ] 栈分配和 Span 操作
- [/] 索引和范围 (C# 8.0+) — 当端点是纯的时候,基本的范围构造被视为纯
- [x] 位移操作和基本的二元/一元运算符
- [ ] Async/await 表达式
- [ ] 不安全代码块
- [ ] 指针操作
### 语句
- [x] 局部声明(带有纯初始化器)
- [x] Return 语句(仅作为最后一条语句,需要纯表达式)
- [/] 表达式语句(纯方法调用、对局部变量的赋值正常。字段/属性赋值视为非纯。)
- [ ] If 语句
- [ ] Switch 语句
- [ ] Throw 语句/表达式 - 视为非纯。
- [ ] Try-catch-finally 块
- [ ] 局部函数(通过调用进行分析。)
- [ ] Using 语句
- [ ] Using 声明 (C# 8.0+)
- [ ] Lock 语句
- [ ] Yield 语句(迭代器方法)
- [ ] Fixed 语句
### 集合和数据结构
- [ ] 不可变集合 (System.Collections.Immutable) - 创建假定为非纯。
- [ ] 只读集合 (IReadOnly\\\* 接口) - 创建假定为非纯。
- [ ] 数组(以只读方式使用时) - 访问/创建假定为非纯。
- [x] 元组(创建)
- [ ] 集合表达式 (C# 12)
- [ ] 可变集合 (List, Dictionary 等) - 创建/修改假定为非纯。
- [ ] 修改集合元素 - 假定为非纯。
- [ ] 内联数组 (C# 12)
### 方法类型
- [x] 常规方法
- [x] 表达式主体方法
- [ ] 扩展方法(通过调用进行分析。)
- [ ] 局部函数(通过调用进行分析。)
- [ ] 抽象方法(已忽略)
- [/] 递归方法(存在循环检测。)
- [ ] 虚/重写方法(像常规方法一样进行分析。)
- [x] 泛型方法(通过符号分析处理。)
- [ ] 异步方法
- [ ] 迭代器方法 (yield return)
- [ ] 不安全方法
- [ ] 运算符重载(通过调用/二元操作进行分析。)
- [ ] 用户定义的转换(通过调用进行分析。)
- [ ] 静态抽象/虚接口成员 (C# 11)
### 类型声明
(分析侧重于方法体,而不是类型本身的纯度)
- [ ] 类
- [ ] 接口
- [ ] 结构体
- [ ] 记录 (C# 9)
- [ ] 记录结构体 (C# 10)
- [ ] 枚举
- [ ] 委托 - 调用假定为非纯。
- [ ] 文件局部类型 (C# 11)
- [ ] 主构造函数 (C# 12)
### 成员声明
- [x] 实例方法
- [x] 静态方法
- [ ] 构造函数 - 尚未分析。
- [/] 属性(仅 get) - 当前访问被视为非纯。
- [/] 自动属性(仅 get 或仅 init) - 当前访问被视为非纯。
- [/] 字段 (readonly) - 读取 `static readonly` 是纯的。读取实例或非 readonly 静态是非纯的。赋值是非纯的。
- [ ] 事件
- [ ] 索引器 - 访问/赋值假定为非纯。
- [ ] 必成员 (C# 11)
- [ ] 分部属性 (C# 13)
### 参数类型
- [x] 值类型
- [x] 引用类型(按值传递)
- [ ] Ref 参数 - 视为非纯。
- [ ] Out 参数 - 视为非纯。
- [x] In 参数
- [ ] Params 数组
- [ ] Params 集合 (C# 13)
- [x] 可选参数
- [ ] Lambda 表达式中的可选参数 (C# 12)
- [x] Ref readonly 参数
### 特殊特性
- [ ] LINQ 方法 - 除非是简单的常量表达式,否则假定为非纯。
- [/] 字符串操作 - 常量是纯的,方法调用遵循调用规则。
- [/] 数学操作 - 基本运算符是纯的,`System.Math` 调用遵循调用规则。
- [x] 元组操作(创建)
- [ ] 转换方法 (Parse, TryParse 等) - 假定为非纯。
- [ ] I/O 操作 (File, Console 等) - 显式标记为非纯。
- [ ] 网络操作 - 显式标记为非纯。
- [ ] 线程/任务操作 - 显式标记为非纯。
- [ ] 随机数生成 - 显式标记为非纯。
- [ ] 事件订阅/调用 - 假定为非纯。
- [ ] 委托调用 - 假定为非纯。
### 高级语言特性
- [ ] 模式匹配
- [ ] Switch 表达式
- [ ] 列表模式 (C# 11)
- [ ] 顶级语句 (C# 9) - 不适用
- [x] 文件范围命名空间 (C# 10) - 隐式支持。
- [ ] 必需成员 (C# 11)
- [x] 可空引用类型注释 (C# 8.0+) - 隐式支持。
- [ ] 调用方信息特性
- [ ] 源生成器 - 不适用
- [x] 分部类/方法 - 隐式支持。
- [x] 全局 using 指令 (C# 10) - 隐式支持。
- [ ] 泛型特性 (C# 11)
- [x] 任意类型的类型别名 (C# 12) - 隐式支持。
- [ ] Experimental 特性 (C# 12)
### C# 11 特定特性
- [x] 扩展的 nameof 作用域
- [x] 数值 IntPtr (nint/nuint)
- [x] 泛型特性
- [x] 无符号右移运算符 (>>>)
- [x] 已检查的用户定义运算符
- [x] 原始字符串字面量
- [x] UTF-8 字符串字面量
- [x] 列表模式
- [x] 文件局部类型
- [x] 必需成员
- [x] 自动默认结构体
- [x] 常量字符串上的模式匹配 Span
- [x] 字符串插值表达式中的换行符
- [x] ref 字段和 scoped ref
- [x] 泛型数学支持 (静态 virtual/abstract 接口成员)
### C# 12 特定特性
- [~] 集合表达式 - _部分支持:仅在创建不可变集合时_
- [x] 主构造函数
- [x] 内联数组
- [ ] Lambda 表达式中的可选参数
- [x] ref readonly 参数
- [ ] 任意类型的类型别名
- [ ] Experimental 特性
- [ ] 拦截器 (预览版)
### C# 13 特定特性
- [ ] params 集合
- [ ] Lock 对象
- [x] 转义序列 \e
- [ ] 方法组自然类型改进
- [ ] 对象初始化器中的隐式索引器访问
- [ ] 迭代器/async 中的 ref/unsafe
- [ ] ref 结构接口
- [ ] 重载决议优先级
- [ ] 分部属性
- [ ] field 上下文关键字 (预览版)
### 字段/属性访问
- [x] Readonly 字段
- [x] Const 字段
- [x] 仅 get 属性
- [x] 仅 init 属性 (C# 9)
- [x] 可变字段
- [x] 带有 set 的属性
- [x] 静态可变字段
- [x] 事件字段
- [x] Volatile 字段(由于其特殊的内存排序语义和线程安全影响,读取和写入均被视为非纯)
### 泛型和高级构造
- [x] 泛型方法
- [ ] 带有约束的泛型类型参数
- [ ] 协变和逆变
- [x] 反射
- [x] 动态类型
- [x] 不安全代码
## 枚举操作
PurelySharp 将枚举视为纯数据类型。以下涉及枚举的操作被认为是纯的:
- 访问枚举值
- 将枚举转换为其基础数值类型
- 比较枚举值
- 使用 `Enum` 类中的方法
请注意,分析器对 `Enum.TryParse()` 进行了特殊处理,尽管使用了 `out` 参数,仍将其视为纯方法。
### 示例
```
public enum Status
{
Pending,
Active,
Completed,
Failed
}
public class EnumOperations
{
[EnforcePure]
public bool IsActiveOrPending(Status status)
{
return status == Status.Active || status == Status.Pending;
}
[EnforcePure]
public int GetStatusCode(Status status)
{
return (int)status;
}
[EnforcePure]
public bool ParseStatus(string value, out Status status)
{
return Enum.TryParse(value, out status);
}
[EnforcePure]
public Status GetStatusFromValue(int value)
{
return (Status)value;
}
}
```
## 委托操作
PurelySharp 支持委托类型和操作。委托的纯度分析侧重于委托的创建及其调用:
### 委托纯度规则
- **委托类型定义**:定义委托类型始终是纯的。
- **委托创建**:
- 从纯方法创建委托是纯的。
- 如果 Lambda 表达式的主体是纯的并且不捕获非纯状态,则创建 Lambda 表达式是纯的。
- 如果匿名方法的主体是纯的并且不捕获非纯状态,则创建匿名方法是纯的。
- **委托调用**:
- 如果委托目标是纯的并且所有参数都是纯的,则调用委托是纯的。
- 如果分析器无法确定委托目标的纯度,它会保守地将调用标记为非纯。
- **委托组合**:
- 如果两个委托操作数都是纯的,则组合委托(`+=`、`+`)是纯的。
- 如果两个委托操作数都是纯的,则移除委托(`-=`、`-`)是纯的。
### 示例
```
// Define delegate types (always pure)
public delegate int Calculator(int x, int y);
public delegate void Logger(string message);
public class DelegateOperations
{
// Pure delegate field
private readonly Func _adder = (x, y) => x + y;
[EnforcePure]
public int Add(int x, int y)
{
// Creating and invoking a pure lambda delegate
Calculator calc = (a, b) => a + b;
return calc(x, y);
}
[EnforcePure]
public IEnumerable ProcessNumbers(IEnumerable numbers)
{
// Using delegates with LINQ (pure)
return numbers.Where(n => n > 0)
.Select(n => n * 2);
}
[EnforcePure]
public Func GetMultiplier(int factor)
{
// Higher-order function returning a pure delegate
return x => x * factor;
}
[EnforcePure]
public int CombineDelegates(int x, int y)
{
// Combining pure delegates
Func doubler = n => n * 2;
Func incrementer = n => n + 1;
// Combined delegate is pure if components are pure
Func combined = n => incrementer(doubler(n));
return combined(x) + combined(y);
}
// This would generate a diagnostic
[EnforcePure]
public void ImpureDelegateExample()
{
int counter = 0;
// Impure delegate - captures and modifies a local variable
Action incrementCounter = () => { counter++; };
// Invoking impure delegate
incrementCounter(); // Analyzer will flag this
}
}
```
请注意,委托调用的分析是保守的。如果分析器无法确定委托的纯度,它会将调用标记为非纯。
## 特性
- [x] `[EnforcePure]` - 标记一个应进行纯度分析的方法
- [x] `[AllowSynchronization]` - 在对只读对象进行同步时,允许纯方法中出现 lock 语句
## 非纯命名空间(始终被视为非纯)
- System.IO
- System.Net
- System.Data
- System.Threading
- System.Threading.Tasks
- System.Diagnostics
- System.Security.Cryptography
- System.Runtime.InteropServices
## 非纯类型(始终被视为非纯)
- Random
- DateTime
- File
- Console
- Process
- Task
- Thread
- Timer
- WebClient
- HttpClient
- StringBuilder
- Socket
- NetworkStream
## 常见非纯操作
- 修改字段或属性
- 读取或写入 volatile 字段
- 使用 Interlocked 操作
- 调用带有副作用的方法
- I/O 操作(文件、控制台、网络)
- 异步操作
- 锁定(线程同步)
- 事件订阅/引发
- 不安全代码和指针操作
- 创建可变集合
## 跨框架和语言版本支持
- [x] C# 8.0+ 语言特性
- [ ] 不同的目标框架(.NET Framework、.NET Core、.NET 5+)
## 示例
### 纯方法示例
分析器确保标记为 `[EnforcePure]` 的方法不包含非纯操作:
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public int PureHelperMethod(int x)
{
return x * 2; // Pure operation
}
[EnforcePure]
public int TestMethod(int x)
{
// Call to pure method is also pure
return PureHelperMethod(x) + 5;
}
}
```
### 非纯方法示例
分析器检测非纯操作并报告诊断:
#### 修改状态
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
private int _state;
[EnforcePure]
public int TestMethod(int value)
{
_state++; // Impure operation: modifies class state
return _state;
}
}
// Analyzer Error: PMA0001 - Method 'TestMethod' is marked as pure but contains impure operations
```
#### I/O 操作
```
using System;
using System.IO;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public void TestMethod(string path)
{
File.WriteAllText(path, "test"); // Impure operation: performs I/O
}
}
// Analyzer Error: PMA0001 - Method 'TestMethod' is marked as pure but contains impure operations
```
#### 控制台输出
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public int TestMethod()
{
Console.WriteLine("Hello World"); // Impure operation: console output
return 42;
}
}
// Analyzer Error: PMA0001 - Method 'TestMethod' is marked as pure but contains impure operations
```
#### 静态字段访问
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
private static string sharedState = "";
[EnforcePure]
public string TestMethod()
{
// Reading from static field is considered impure
return sharedState;
}
}
// Analyzer Error: PMA0001 - Method 'TestMethod' is marked as pure but contains impure operations
```
#### Volatile 字段访问
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
private volatile int _counter;
[EnforcePure]
public int GetCounter()
{
// Both reading and writing to volatile fields is considered impure
// due to their special memory ordering semantics
return _counter;
}
[EnforcePure]
public void UpdateCounter(int value)
{
_counter = value; // Writing to volatile field is impure
}
}
// Analyzer Error: PMA0001 - Method 'GetCounter' is marked as pure but contains impure operations
// Analyzer Error: PMA0001 - Method 'UpdateCounter' is marked as pure but contains impure operations
```
#### Volatile 字段的线程同步
```
using System;
using System.Threading;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class AllowSynchronizationAttribute : Attribute { }
public class TestClass
{
private volatile bool _initialized;
private readonly object _lock = new object();
[EnforcePure]
[AllowSynchronization] // Even with AllowSynchronization, volatile read is impure
public void EnsureInitialized()
{
if (!_initialized) // Reading volatile field is impure
{
lock (_lock)
{
if (!_initialized) // Reading volatile field again is impure
{
Initialize();
_initialized = true; // Writing to volatile field is impure
}
}
}
}
private void Initialize() { /* ... */ }
}
// Analyzer Error: PMA0001 - Method 'EnsureInitialized' is marked as pure but contains impure operations
```
#### 使用 Interlocked 的原子操作
```
using System;
using System.Threading;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
private volatile int _counter;
[EnforcePure]
public int IncrementAndGet()
{
// Using Interlocked with volatile fields is impure
// since it modifies shared state in a thread-safe manner
return Interlocked.Increment(ref _counter);
}
[EnforcePure]
public int CompareExchange(int newValue, int comparand)
{
// All Interlocked operations are impure
return Interlocked.CompareExchange(ref _counter, newValue, comparand);
}
}
// Analyzer Error: PMA0001 - Method 'IncrementAndGet' is marked as pure but contains impure operations
// Analyzer Error: PMA0001 - Method 'CompareExchange' is marked as pure but contains impure operations
```
### 更复杂的示例
#### LINQ 操作(纯)
LINQ 操作通常被认为是纯的,因为它们处理数据的不可变视图:
```
using System;
using System.Linq;
using System.Collections.Generic;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public IEnumerable FilterAndTransform(IEnumerable numbers)
{
// LINQ operations are pure
return numbers
.Where(n => n > 10)
.Select(n => n * 2)
.OrderBy(n => n);
}
}
// No analyzer errors - method is pure
```
#### 迭代器方法(纯)
使用 `yield return` 的迭代器方法可以是纯的:
```
using System;
using System.Collections.Generic;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public IEnumerable GenerateFibonacciSequence(int count)
{
int a = 0, b = 1;
for (int i = 0; i < count; i++)
{
yield return a;
(a, b) = (b, a + b); // Tuple deconstruction for swapping
}
}
}
// No analyzer errors - method is pure
```
#### 带有 AllowSynchronization 的 Lock 语句
当使用 `[AllowSynchronization]` 特性时,纯方法中允许出现 lock 语句:
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class AllowSynchronizationAttribute : Attribute { }
public class TestClass
{
private readonly object _lockObj = new object();
private readonly Dictionary _cache = new Dictionary();
[EnforcePure]
[AllowSynchronization]
public int GetOrComputeValue(string key, Func computeFunc)
{
lock (_lockObj) // Normally impure, but allowed with [AllowSynchronization]
{
if (_cache.TryGetValue(key, out int value))
return value;
// Compute is allowed if the function is pure
int newValue = computeFunc(key);
_cache[key] = newValue; // This would be impure without [AllowSynchronization]
return newValue;
}
}
}
// No analyzer errors with [AllowSynchronization]
```
#### Switch 表达式和模式匹配
支持现代 C# 模式匹配和 switch 表达式:
```
using System;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class Shape { }
public class Circle : Shape { public double Radius { get; } }
public class Rectangle : Shape { public double Width { get; } public double Height { get; } }
public class TestClass
{
[EnforcePure]
public double CalculateArea(Shape shape)
{
// Switch expression with pattern matching
return shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
_ => throw new ArgumentException("Unknown shape type")
};
}
}
// No analyzer errors - method is pure
```
#### 使用不可变集合
使用不可变集合的方法保持纯度:
```
using System;
using System.Collections.Immutable;
using System.Linq;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public ImmutableDictionary AddToCounters(
ImmutableDictionary counters,
string key)
{
// Working with immutable collections preserves purity
if (counters.TryGetValue(key, out int currentCount))
return counters.SetItem(key, currentCount + 1);
else
return counters.Add(key, 1);
// Note: The original collection is not modified
}
}
// No analyzer errors - method is pure
```
#### 包含多个纯操作的复杂方法
结合多个纯操作的复杂方法:
```
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
[AttributeUsage(AttributeTargets.Method)]
public class EnforcePureAttribute : Attribute { }
public class TestClass
{
[EnforcePure]
public (double Average, int Min, int Max, ImmutableList Filtered) AnalyzeData(
IEnumerable data, int threshold)
{
// Local function (must also be pure)
bool IsOutlier(int value) => value < 0 || value > 1000;
// Use LINQ to process the data
var filteredData = data
.Where(x => !IsOutlier(x) && x >= threshold)
.ToImmutableList();
if (!filteredData.Any())
throw new ArgumentException("No valid data points after filtering");
// Multiple computations on the filtered data
var average = filteredData.Average();
var min = filteredData.Min();
var max = filteredData.Max();
// Return a tuple with the results
return (Average: average, Min: min, Max: max, Filtered: filteredData);
}
}
// No analyzer errors - method is pure
```
### 限制和边缘情况
以下示例演示了分析器可能无法正确识别非纯操作的情况:
#### 间接方法非纯性
```
```
## 构造函数分析
当将 `[EnforcePure]` 应用于构造函数时,分析器应用特殊规则以适应构造函数的独特目的。标记为纯的构造函数必须遵循以下规则:
1. **允许实例字段/属性赋值**:与常规方法不同,构造函数可以将值赋给包含类型的实例字段和属性。
2. **不允许修改静态字段**:修改静态字段仍被视为非纯,因为这会影响所构造实例之外的状态。
3. **不允许调用非纯方法**:不允许从纯构造函数调用非纯方法(如 I/O 操作)。
4. **允许集合初始化**:如果将集合分配给实例字段,则允许创建和初始化集合(例如,`new List { 1, 2, 3 }`)。
5. **基构造函数调用**:如果构造函数调用基构造函数,则该基构造函数也必须是纯的。
### 示例
#### 纯构造函数
```
[AttributeUsage(AttributeTargets.Constructor)]
public class EnforcePureAttribute : Attribute { }
public class Person
{
private readonly string _name;
private readonly int _age;
private readonly List _skills;
[EnforcePure]
public Person(string name, int age)
{
_name = name;
_age = age;
_skills = new List(); // Allowed: initializing a collection field
}
}
```
#### 非纯构造函数(修改静态字段)
```
public class Counter
{
private static int _instanceCount = 0;
private readonly int _id;
[EnforcePure]
public Counter() // Error: Modifies static state
{
_id = ++_instanceCount; // Impure: modifies static field
}
}
```
#### 非纯构造函数(调用非纯方法)
```
public class Logger
{
private readonly string _name;
[EnforcePure]
public Logger(string name) // Error: Calls an impure method
{
_name = name;
InitializeLog(); // Calls impure method
}
private void InitializeLog()
{
Console.WriteLine($"Logger {_name} initialized"); // Impure operation
}
}
```
## 跨框架和语言版本支持
```
## Demo 项目
A ready-to-run demo app is included in the solution: `PurelySharp.Demo`.
- What it shows
- PS0002: Methods marked `[EnforcePure]` performing impure operations (state mutation, I/O, volatile reads, array mutation)
- PS0004: Methods that appear pure but are missing `[EnforcePure]`
- PS0003 is intentionally not demonstrated in the demo to keep the focus on core purity rules
- How it’s wired
- References `PurelySharp.Analyzer` and `PurelySharp.CodeFixes` as analyzers via project references
- References `PurelySharp.Attributes` as a normal project reference
- Local `.editorconfig` in `PurelySharp.Demo` tunes severities: PS0002=warning, PS0004=suggestion, PS0003=none
- Run the demo
```powershell
# Build the whole solution (ensures analyzers are built)
dotnet build .\PurelySharp.sln -c Release
# Build just the demo project
dotnet build .\PurelySharp.Demo\PurelySharp.Demo.csproj -c Release
```
- 在 Visual Studio 中
- 安装 VSIX(见上文)以便在编辑时获取实时诊断
- 打开 `PurelySharp.Demo` 并检查 `Program.cs` 以查看内联诊断
标签:Analyzer, EnforcePure, Pure, Roslyn, SOC Prime, Visual Studio, 代码规范, 多人体追踪, 安全专业人员, 属性标记, 开发工具, 数据管道, 方法纯度, 无副作用, 纯函数, 编译器, 软件工程, 错误基检测, 静态代码分析, 静态检查