要将流水号生成逻辑实现为一个 C# 特性(Attribute),你可以通过自定义属性来实现。自定义属性通常用于元数据标注,但在这种情况下,你可以创建一个特性来生成并管理流水号。
以下是一个示例,展示如何创建一个自定义特性来生成流水号:
### 1. 创建自定义特性
首先,创建一个自定义特性类 `SerialNumberAttribute`:
```csharp
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class SerialNumberAttribute : Attribute
{
private static readonly object _lock = new object();
private static int _currentNumber = 0;
private static DateTime _lastGeneratedDate = DateTime.MinValue;
public string SerialNumber { get; private set; }
public SerialNumberAttribute()
{
lock (_lock)
{
var currentDate = DateTime.Now;
if (_lastGeneratedDate.Year != currentDate.Year || _lastGeneratedDate.Month != currentDate.Month)
{
_currentNumber = 1;
_lastGeneratedDate = currentDate;
}
else
{
_currentNumber++;
}
SerialNumber = $"F{currentDate:yyMM}{_currentNumber:0000}";
}
}
}
```
### 2. 使用自定义特性
你可以在类或方法上使用这个自定义特性:
```csharp
[SerialNumber]
public class MyClass
{
public void PrintSerialNumber()
{
var serialNumberAttribute = (SerialNumberAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(SerialNumberAttribute));
Console.WriteLine($"Generated Serial Number: {serialNumberAttribute.SerialNumber}");
}
}
```
### 3. 示例用法
以下是一个示例展示如何使用和测试这个自定义特性:
```csharp
class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.PrintSerialNumber();
// Simulate creation of another instance in the same month
var anotherClass = new MyClass();
anotherClass.PrintSerialNumber();
// Simulate creation of another instance in the next month
System.Threading.Thread.Sleep(1000); // Sleep for a second to simulate time passing
var nextMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month + 1, 1);
SystemClock.SetDateTime(nextMonth); // Assuming you have a way to mock the system clock
var nextMonthClass = new MyClass();
nextMonthClass.PrintSerialNumber();
}
}
```
### 注意事项
1. **线程安全**:在特性中使用了 `lock` 关键字来确保线程安全。
2. **日期重置**:每当月份变化时,流水号会从 `0001` 开始重新计数。
3. **持久化**:如果需要跨应用程序运行持久化流水号(例如,重启后仍然保持计数),你需要将计数存储在数据库或文件中,而不是仅在内存中。
### 持久化实现
如果需要持久化流水号,可以将计数存储到数据库中,以下是一个示例:
```csharp
public sealed class SerialNumberAttribute : Attribute
{
private static readonly object _lock = new object();
private static ApplicationDbContext _context;
public string SerialNumber { get; private set; }
public SerialNumberAttribute()
{
_context = new ApplicationDbContext(); // 假设你有一个无参构造函数
lock (_lock)
{
var currentDate = DateTime.Now;
var year = currentDate.Year;
var month = currentDate.Month;
var serialNumber = _context.SerialNumbers
.FirstOrDefault(sn => sn.Year == year && sn.Month == month);
if (serialNumber == null)
{
serialNumber = new SerialNumber
{
Year = year,
Month = month,
CurrentNumber = 1
};
_context.SerialNumbers.Add(serialNumber);
}
else
{
serialNumber.CurrentNumber++;
}
_context.SaveChanges();
SerialNumber = $"F{year % 100:00}{month:00}{serialNumber.CurrentNumber:0000}";
}
}
}
```
通过以上方法,你可以创建一个自定义特性来生成流水号,并且可以根据需要将流水号持久化到数据库中。