原理:获取系统时间,并在其后加上一个随机数
sample:
将IdentityGenerator实例化后可直接获取
Embacle.Identity.IdentityGenerator ig = new Embacle.Identity.IdentityGenerator();
long identity = ig.GetIdentity();//identity就是生成的Id
也可以直接获取IdentityGenerator的静态实例,这样整个应用程序域内就只有一个实例。结果是一样的,内存挑剔者使用
long identity = Embacle.Identity.IdentityGenerator.GetInstance().GetIdentity();
输出样例:前8位是时间戳,刻度取到秒(TimestampStyle.SecondTicks),后3位是随机数,设置随机数长度为3下 面是GetIdentity()输出结果样例,如果精度要求更高的话可以自行增加随机数长度或者刻度值,可通过GetItentity的重载方法实现。但 程序没有捕捉Int64整型数字溢出异常,也就是说,如果设置时间戳刻度太精细或者随机数长度过长都可能导致溢出的异常。
如果要自定义该异常请自行更改GetIdentity(DateTime,long,int)签名的方法,在其中加入自己的验证逻辑,不过使用默认 GenIdentity()是不会抛出这样的异常的。理论上支持1秒钟生成1000个不重复Id,但是作者自行测试发现在理论需求达到非得在1秒钟生成 900个左右的Id或者更高的话,设置随机数长度3会导致重复检查率大大增加,如果有这一很BT的需求的话请考虑增加随机数长度试试看,或者增加时间戳刻 度。我发现一般PC机的时钟刻度只能精确到10毫秒,要比10毫秒更精确本人没有任何办法。
21047037328
21047038123
21047039547
21047040005
21047038927
自定义时间戳刻度:
注意:三个参数:
startDateTime 起始时间
(默认为:2005-01-01 00:00 000)
TimestampStyle 是时间戳刻度枚举,为方便设置自定义时间戳刻度,如果能看懂换算值的话也可以自行输入具体刻度值 TimestampStyleTicks是自行输入时间戳刻度
(默认为:TimestampStyle.SecondTicks,即取到秒)
randomLength 是随机数长度
(默认为:3,即0-999)
Embacle.Identity.IdentityGenerator ig = new Embacle.Identity.IdentityGenerator();
long identity = ig.GetIdentity(4);自定义随机数长度为4
long identity = ig.GetIdentity(TimestampStyle.TenMillSecondTicks);自定义随机数长度为10毫秒精度
long identity = ig.GetIdentity(TimestampStyle.TenMillSecondTicks,4)同时设置上面两种精度
匆匆完成,请仔细斟酌使用,如果有改进意见或者想骂人可以直接回复该贴,或者发送邮件给我,注意这体现了您的个人修养
作者:Janssenkm
信箱:janssenkm#gmail.com(请将#改为@)
代码如下:
// 原理:获取系统时间,并在其后加上一个随机数
//
// sample:
// 将IdentityGenerator实例化后可直接获取
// Embacle.Identity.IdentityGenerator ig = new Embacle.Identity.IdentityGenerator();
// long identity = ig.GetIdentity();// identity就是生成的Id
//
// 也可以直接获取IdentityGenerator的静态实例,这样整个应用程序域内就只有一个实例。结果是一样的,内存挑剔者使用
// long identity = Embacle.Identity.IdentityGenerator.GetInstance().GetIdentity();
//
//
// 输出样例:前8位是时间戳,刻度取到秒(TimestampStyle.SecondTicks),后3位是随机数,设置随机数长度为3
// 下面是GetIdentity()输出结果样例,如果精度要求更高的话可以自行增加随机数长度或者刻度值,可通过GetItentity
// 的重载方法实现。但程序没有捕捉Int64整型数字溢出异常,也就是说,如果设置时间戳刻度太精细或者随机数长
// 度过长都可能导致溢出的异常。
//
// 如果要自定义该异常请自行更改GetIdentity(DateTime,long,int)签名的方法,在其中加入自己的验证逻辑,不过
// 使用默认GenIdentity()是不会抛出这样的异常的。理论上支持1秒钟生成1000个不重复Id,但是作者自行测试发现
// 在理论需求达到非得在1秒钟生成900个左右的Id或者更高的话,设置随机数长度3会导致重复检查率大大增加,如果
// 有这一很BT的需求的话请考虑增加随机数长度试试看,或者增加时间戳刻度。我发现一般PC机的时钟刻度只能精确到
// 10毫秒,要比10毫秒更精确本人没有任何办法。
//
// 21047037328
// 21047038123
// 21047039547
// 21047040005
// 21047038927
//
// 自定义时间戳刻度:
// 注意:三个参数:
// startDateTime 起始时间
// (默认为:2005-01-01 00:00 000)
// TimestampStyle 是时间戳刻度枚举,为方便设置自定义时间戳刻度,如果能看懂换算值的话也可以自行输入具体刻度值 TimestampStyleTicks是自行输入时间戳刻度
// (默认为:TimestampStyle.SecondTicks,即取到秒)
// randomLength 是随机数长度
// (默认为:3,即0-999)
// Embacle.Identity.IdentityGenerator ig = new Embacle.Identity.IdentityGenerator();
// long identity = ig.GetIdentity(4);// 自定义随机数长度为4
// long identity = ig.GetIdentity(TimestampStyle.TenMillSecondTicks);// 自定义随机数长度为10毫秒精度
// long identity = ig.GetIdentity(TimestampStyle.TenMillSecondTicks,4)// 同时设置上面两种精度
// 匆匆完成,请仔细斟酌使用,如果有改进意见或者想骂人可以直接回复该贴,或者发送邮件给我,注意这体现了您的个人修养
//
// 作者:Janssenkm
// 信箱:janssenkm#gmail.com(请将#改为@)
using System;
using System.Text;
using System.Collections;
namespace Embacle.Itentity
{
///
/// 简单Id生成器
///
public class IdentityGenerator
{
///
/// 随机数缓存
///
private static Hashtable ht;
///
/// 时间戳刻度缓存
///
private long lastTimeStampStyleTicks;
///
/// 时间戳缓存(上一次计算ID的系统时间按时间戳刻度取值)
///
private long lastEndDateTimeTicks;
public IdentityGenerator()
{
if(ht==null )
ht = new Hashtable();
}
///
/// IdentityGenerator的静态实例
///
private static IdentityGenerator ig;
public IdentityGenerator GetInstance()
{
if(ig==null )
ig = new IdentityGenerator();
return ig;
}
///
/// 按照时间戳刻度计算当前时间戳
///
/// 起始时间
/// 时间戳刻度值
///
private long GetTimestamp(DateTime startDateTime,long timestampStyleTicks)
{
if(timestampStyleTicks==0 )
throw new Exception("时间戳刻度样式精度值不符,不能为0或负数" );
DateTime endDateTime = DateTime.Now;
long ticks = (endDateTime.Ticks - startDateTime.Ticks)/ timestampStyleTicks;
return ticks;
}
///
/// 静态随机数生成器
///
private static Random random;
///
/// 获取随机数
///
/// 随机数长度
///
private long GetRandom(int length)
{
if(length<=0 )
throw new Exception("随机数长度指派错误,长度不能为0或负数" );
if(random==null )
random = new Random();
int minValue = 0 ;
int maxValue = int.Parse(System.Math.Pow(10 ,length).ToString());
long result = long .Parse(random.Next(minValue,maxValue).ToString());
return result;
}
///
/// 计算一个Id
/// 以2005-1-1 00:00 000为起始时间刻度
///
///
public long GetIdentity()
{
DateTime startDateTime = new DateTime(2005,1,1,0,0,0,0 );
TimestampStyle timestampStyle = TimestampStyle.SecondTicks;
int randomLength = 3 ;
return GetIdentity(startDateTime,timestampStyle,randomLength);
}
///
/// 计算一个Id
/// 以2005-1-1 00:00 000为起始时间刻度
///
/// 时间戳刻度
///
public long GetIdentity(TimestampStyle timestampStyle)
{
DateTime startDateTime = new DateTime(2005,1,1,0,0,0,0 );
int randomLength = 3 ;
return GetIdentity(startDateTime,timestampStyle,randomLength);
}
///
/// 计算一个Id
///
/// 随机数长度
///
public long GetIdentity(int randomLength)
{
DateTime startDateTime = new DateTime(2005,1,1,0,0,0,0 );
TimestampStyle timestampStyle = TimestampStyle.SecondTicks;
return GetIdentity(startDateTime,timestampStyle,randomLength);
}
///
/// 计算一个Id
///
/// 时间戳刻度
/// 随机数长度
///
public long GetIdentity(TimestampStyle timestampStyle,int randomLength)
{
DateTime startDateTime = new DateTime(2005,1,1,0,0,0,0 );
return GetIdentity(startDateTime,timestampStyle,randomLength);
}
///
/// 计算一个Id
///
/// 时间戳的起始时间
/// 时间戳刻度
/// 随机数长度
///
public long GetIdentity(DateTime startDateTime,TimestampStyle timestampStyle,int randomLength)
{
long timestampStyleTicks = long.Parse(timestampStyle.ToString("D" ));
return GetIdentity(startDateTime,timestampStyleTicks,randomLength);
}
///
/// 计算一个Id
///
/// 时间戳的起始时间
/// 时间戳刻度(毫微秒单位)
/// 随机数长度
///
public long GetIdentity(DateTime startDateTime,long timestampStyleTicks,int randomLength)
{
// 新一轮时间戳刻度更新后更新缓存
//如果该参数不变则不进行此更新
if(timestampStyleTicks!= lastTimeStampStyleTicks)
ht.Clear();
//取得时间戳(当前时间按刻度取值)
long timestamp = GetTimestamp(startDateTime,timestampStyleTicks);
//新一轮时间戳更新后更新缓存
if(timestamp!= lastEndDateTimeTicks)
ht.Clear();
//幂
long power = long.Parse(Math.Pow(10 ,randomLength).ToString());
//随机数
long rand = GetRandom(randomLength);
//生成结果(Id)
long result = timestamp * power + rand;
//如果发现重复
if (ht.ContainsKey(result))
{
//在随机数长度范围内再重复查找一次
for(int i=0;i<power;i++ )
{
rand = GetRandom(randomLength);
result = timestamp * power + rand;
//发现非重复的Id
if(! ht.ContainsKey(result))
{
//将新的Id加入HashTable缓存
ht.Add(result,result);
break;//找到一个同一时间戳内的Id即退出
}
}
// 此处运行在当前时间戳内无法再继续生成Id的代码,如:
//
//throw new Exception("已无法生成更多Id,请增加时间戳刻度TimestampStyle或增加随机数长度randomLength");
}
else
{
//将新的Id加入HashTable缓存
ht.Add(result,result);
}
//记录当前一轮时间戳(当前时间按刻度取值)
this.lastEndDateTimeTicks = timestamp;
//记录当前一轮时间戳刻度
this.lastTimeStampStyleTicks = timestampStyleTicks;
return result;
}
}
///
/// 时间戳精度样式
/// 采用格里高利时间刻度单位-毫微秒(1秒 =10,000,000毫微秒 )
/// 疑问:MSDN上写的一毫微秒为一百万分之一秒 =100,000,000毫微秒,但是我始终无法算出该值,
/// 不知是我错了还是MSDN的问题实际得出的数字是1秒 =10,000,000毫微秒,认为是我错了的话请将
/// 下面的每一项枚举值都加一个0即可
///
public enum TimestampStyle:long
{
///
/// 时间刻度精度取为1毫秒(此项无意义,因为一般PC机系统时钟只能精确到10毫秒)
///
MillSecondTicks = 10000 ,
///
/// 时间刻度精度取为10毫秒,这是一般PC机系统时钟的最小精度单位
///
TenMillSecondTicks = 100000 ,
///
/// 时间刻度精度取为100毫秒
///
HundredMillSecondTicks = 1000000 ,
///
/// 时间刻度精度取为1秒,即1000毫秒
///
SecondTicks = 10000000 ,
///
/// 时间刻度精度取为5秒
///
FiveSecondTicks = 50000000 ,
///
/// 时间刻度精度取为10秒
///
TenSecondTicks = 100000000 ,
///
/// 时间刻度精度取为1分种(60秒)
///
MinutesTicks = 600000000
}
}
经过一个并发测试:设置时间刻度为1秒,随机数长度为3,可以保证1秒钟内生成1000个不重复的ID。如果要求每秒生成100000个不重复的刻度,则设置随机数长度为5即可。
使用方法
protected void Button1_Click(object sender, EventArgs e)
{
IdentityGenerator ig = new IdentityGenerator();
long identity = ig.GetIdentity(TimestampStyle.SecondTicks, 2);//同时设置上面两种精度
this.lbText.Text =identity.ToString("x");
}
其他资料转换为16进制
8. 转换成十六进制
任何数据在计算机内部都是以二进制保存的,所以进制与数据的存储无关,只与输入输出有关。所以,对于进制转换,我们只关心字符串中的结果。
在上面的第 4 条中提到了 ToString() 方法可以将数值转换成字符串,不过在字符串中,结果是以十进制显示的。现在我们带给它加一些参数,就可以将其转换成十六进制——使用 ToString(string) 方法。
这里需要一个 string 类型的参数,这就是格式说明符。十六进制的格式说明符是 "x" 或者 "X",使用这两种格式说明符的区别主要在于 A-F 六个数字:"x" 代表 a-f 使用小写字母表示,而 "X" 而表示 A-F 使用大字字母表示。如下例:
private void TestHex() {
int a = 188;
this.textBox1.Text = "";
this.textBox1.AppendText("a(10) = " + a.ToString() + "\n");
this.textBox1.AppendText("a(16) = " + a.ToString("x") + "\n");
this.textBox1.AppendText("a(16) = " + a.ToString("X") + "\n");
}
运行结果如下:
a(10) = 188
a(16) = bc
a(16) = BC
这时候,我们可能有另一种需求,即为了显示结果的整齐,我们需要控制十六进制表示的长度,如果长度不够,用前导的 0 填补。解决这个问题,我们只需要在格式说明符“x”或者“X”后写上表示长度的数字就行了。比如,要限制在 4 个字符的长度,可以写成“X4”。在上例中追加一句:
this.textBox1.AppendText("a(16) = " + a.ToString("X4") + "\n");
其结果将输出 a(16) = 00BC。
现在,我们还要说一说如何将一个表示十六进制数的字符串转换成整型。这一转换,同样需要借助于 Parse() 方法。这里,我需要 Parse(string, System.Globalization.NumberStyles) 方法。第一个参数是表示十六进制数的字符串,如“AB”、“20”(表示十进制的 32) 等。第二个参数 System.Globalization.NumberStyles 是一个枚举类型,用来表示十六进制的枚举值是 HexNumber。因此,如果我们要将“AB”转换成整型,就应该这样写:int b = int.Parse("AB", System.Globalization.NumberStyles.HexNumber),最后得到的 b 的值是 171。