C#生成string类型的唯一ID

using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace 命名空间
{
    /// <summary>
    /// Represents an ObjectId (see also BsonObjectId).
    /// </summary>
    [Serializable]
    public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>
    {
        #region private static fields
        private static ObjectId emptyInstance = default(ObjectId);
        private static int staticMachine;
        private static short staticPid;
        private static int staticIncrement; // high byte will be masked out when generating new ObjectId
        #endregion

        #region private fields
        // we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap
        // the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification
        // an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems
        private int timestamp;
        private int machine;
        private short pid;
        private int increment;
        #endregion

        #region static constructor
        static ObjectId()
        {
            staticMachine = GetMachineHash();//获取机器名称
            staticPid = (short)Process.GetCurrentProcess().Id; // use low order two bytes only 
            //取得新的 Process 元件,並將它與目前作用中的處理序相關聯。
            staticIncrement = (new Random()).Next();//返回非负随机数
        }
        #endregion

        #region constructors
        /// <summary>
        /// Initializes a new instance of the ObjectId class.
        /// </summary>
        /// <param name="bytes">The value.</param>
        public ObjectId(
            byte[] bytes
        )
        {
            Unpack(bytes, out timestamp, out machine, out pid, out increment);
        }

        /// <summary>
        /// Initializes a new instance of the ObjectId class.
        /// </summary>
        /// <param name="timestamp">The timestamp.</param>
        /// <param name="machine">The machine hash.</param>
        /// <param name="pid">The PID.</param>
        /// <param name="increment">The increment.</param>
        public ObjectId(
            int timestamp,
            int machine,
            short pid,
            int increment
        )
        {
            this.timestamp = timestamp;
            this.machine = machine;
            this.pid = pid;
            this.increment = increment;
        }

        /// <summary>
        /// Initializes a new instance of the ObjectId class.
        /// </summary>
        /// <param name="value">The value.</param>
        public ObjectId(
            string value
        )
        {
            Unpack(Utils.ParseHexString(value), out timestamp, out machine, out pid, out increment);
        }
        #endregion

        #region public static properties
        /// <summary>
        /// Gets an instance of ObjectId where the value is empty.
        /// </summary>
        public static ObjectId Empty
        {
            get { return emptyInstance; }
        }
        #endregion

        #region public properties
        /// <summary>
        /// Gets the timestamp.
        /// </summary>
        public int Timestamp
        {
            get { return timestamp; }
        }

        /// <summary>
        /// Gets the machine.
        /// </summary>
        public int Machine
        {
            get { return machine; }
        }

        /// <summary>
        /// Gets the PID.
        /// </summary>
        public short Pid
        {
            get { return pid; }
        }

        /// <summary>
        /// Gets the increment.
        /// </summary>
        public int Increment
        {
            get { return increment; }
        }

        /// <summary>
        /// Gets the creation time (derived from the timestamp).
        /// </summary>
        public DateTime CreationTime
        {
            get { return Constants.UnixEpoch.AddSeconds(timestamp); }
        }
        #endregion

        #region public operators
        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId</param>
        /// <returns>True if the first ObjectId is less than the second ObjectId.</returns>
        public static bool operator <(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return lhs.CompareTo(rhs) < 0;
        }

        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId</param>
        /// <returns>True if the first ObjectId is less than or equal to the second ObjectId.</returns>
        public static bool operator <=(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return lhs.CompareTo(rhs) <= 0;
        }

        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId.</param>
        /// <returns>True if the two ObjectIds are equal.</returns>
        public static bool operator ==(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return lhs.Equals(rhs);
        }

        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId.</param>
        /// <returns>True if the two ObjectIds are not equal.</returns>
        public static bool operator !=(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return !lhs.Equals(rhs);
        }

        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId</param>
        /// <returns>True if the first ObjectId is greather than or equal to the second ObjectId.</returns>
        public static bool operator >=(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return lhs.CompareTo(rhs) >= 0;
        }

        /// <summary>
        /// Compares two ObjectIds.
        /// </summary>
        /// <param name="lhs">The first ObjectId.</param>
        /// <param name="rhs">The other ObjectId</param>
        /// <returns>True if the first ObjectId is greather than the second ObjectId.</returns>
        public static bool operator >(
            ObjectId lhs,
            ObjectId rhs
        )
        {
            return lhs.CompareTo(rhs) > 0;
        }
        #endregion

        #region public static methods
        /// <summary>
        /// Generates a new ObjectId with a unique value.
        /// </summary>
        /// <returns>A ObjectId.</returns>
        public static ObjectId GenerateNewId()
        {
            int timestamp = GetCurrentTimestamp();//根据时间获取
            int increment = Interlocked.Increment(ref ObjectId.staticIncrement) & 0x00ffffff; // only use low order 3 bytes
            return new ObjectId(timestamp, staticMachine, staticPid, increment);
        }

        public static string NewId()
        {
            return GenerateNewId().ToString();
        }

        /// <summary>
        /// Packs the components of an ObjectId into a byte array.
        /// </summary>
        /// <param name="timestamp">The timestamp.</param>
        /// <param name="machine">The machine hash.</param>
        /// <param name="pid">The PID.</param>
        /// <param name="increment">The increment.</param>
        /// <returns>A byte array.</returns>
        public static byte[] Pack(
            int timestamp,
            int machine,
            short pid,
            int increment
        )
        {
            byte[] bytes = new byte[12];
            bytes[0] = (byte)(timestamp >> 24);
            bytes[1] = (byte)(timestamp >> 16);
            bytes[2] = (byte)(timestamp >> 8);
            bytes[3] = (byte)(timestamp);
            bytes[4] = (byte)(machine >> 16);
            bytes[5] = (byte)(machine >> 8);
            bytes[6] = (byte)(machine);
            bytes[7] = (byte)(pid >> 8);
            bytes[8] = (byte)(pid);
            bytes[9] = (byte)(increment >> 16);
            bytes[10] = (byte)(increment >> 8);
            bytes[11] = (byte)(increment);
            return bytes;
        }

        /// <summary>
        /// Parses a string and creates a new ObjectId.
        /// </summary>
        /// <param name="s">The string value.</param>
        /// <returns>A ObjectId.</returns>
        public static ObjectId Parse(
            string s
        )
        {
            ObjectId objectId;
            if (TryParse(s, out objectId))
            {
                return objectId;
            }
            else
            {
                var message = string.Format("'{0}' is not a valid 24 digit hex string.", s);
                throw new FormatException(message);
            }
        }

        /// <summary>
        /// Tries to parse a string and create a new ObjectId.
        /// </summary>
        /// <param name="s">The string value.</param>
        /// <param name="objectId">The new ObjectId.</param>
        /// <returns>True if the string was parsed successfully.</returns>
        public static bool TryParse(
            string s,
            out ObjectId objectId
        )
        {
            if (s != null && s.Length == 24)
            {
                byte[] bytes;
                if (Utils.TryParseHexString(s, out bytes))
                {
                    objectId = new ObjectId(bytes);
                    return true;
                }
            }

            objectId = default(ObjectId);
            return false;
        }

        /// <summary>
        /// Unpacks a byte array into the components of an ObjectId.
        /// </summary>
        /// <param name="bytes">A byte array.</param>
        /// <param name="timestamp">The timestamp.</param>
        /// <param name="machine">The machine hash.</param>
        /// <param name="pid">The PID.</param>
        /// <param name="increment">The increment.</param>
        public static void Unpack(
            byte[] bytes,
            out int timestamp,
            out int machine,
            out short pid,
            out int increment
        )
        {
            if (bytes.Length != 12)
            {
                throw new ArgumentOutOfRangeException("Byte array must be 12 bytes long.");
            }
            timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
            machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
            pid = (short)((bytes[7] << 8) + bytes[8]);
            increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
        }
        #endregion

        #region private static methods
        private static int GetMachineHash()
        {
            var hostName = Environment.MachineName; // use instead of Dns.HostName so it will work offline
            var md5 = MD5.Create();//哈希算法的默认实现的实例
            var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(hostName));//在派生类中重写时,将指定的System.String中的所有字符编码
            return (hash[0] << 16) + (hash[1] << 8) + hash[2]; // use first 3 bytes of hash
        }

        private static int GetCurrentTimestamp()
        {
            DateTime now = DateTime.UtcNow;
            return (int)Math.Floor((now - Constants.UnixEpoch).TotalSeconds);
        }
        #endregion

        #region public methods
        /// <summary>
        /// Compares this ObjectId to another ObjectId.
        /// </summary>
        /// <param name="other">The other ObjectId.</param>
        /// <returns>A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.</returns>
        public int CompareTo(
            ObjectId other
        )
        {
            int r = timestamp.CompareTo(other.timestamp);
            if (r != 0) { return r; }
            r = machine.CompareTo(other.machine);
            if (r != 0) { return r; }
            r = pid.CompareTo(other.pid);
            if (r != 0) { return r; }
            return increment.CompareTo(other.increment);
        }

        /// <summary>
        /// Compares this ObjectId to another ObjectId.
        /// </summary>
        /// <param name="rhs">The other ObjectId.</param>
        /// <returns>True if the two ObjectIds are equal.</returns>
        public bool Equals(
            ObjectId rhs
        )
        {
            return
                this.timestamp == rhs.timestamp &&
                this.machine == rhs.machine &&
                this.pid == rhs.pid &&
                this.increment == rhs.increment;
        }

        /// <summary>
        /// Compares this ObjectId to another object.
        /// </summary>
        /// <param name="obj">The other object.</param>
        /// <returns>True if the other object is an ObjectId and equal to this one.</returns>
        public override bool Equals(
            object obj
        )
        {
            if (obj is ObjectId)
            {
                return Equals((ObjectId)obj);
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Gets the hash code.
        /// </summary>
        /// <returns>The hash code.</returns>
        public override int GetHashCode()
        {
            int hash = 17;
            hash = 37 * hash + timestamp.GetHashCode();
            hash = 37 * hash + machine.GetHashCode();
            hash = 37 * hash + pid.GetHashCode();
            hash = 37 * hash + increment.GetHashCode();
            return hash;
        }

        /// <summary>
        /// Converts the ObjectId to a byte array.
        /// </summary>
        /// <returns>A byte array.</returns>
        public byte[] ToByteArray()
        {
            return Pack(timestamp, machine, pid, increment);
        }

        /// <summary>
        /// Returns a string representation of the value.
        /// </summary>
        /// <returns>A string representation of the value.</returns>
        public override string ToString()
        {
            return Utils.ToHexString(Pack(timestamp, machine, pid, increment));
        }
        #endregion


        internal static class Utils
        {
            /// <summary>
            /// Tries to parse a hex string to a byte array.
            /// </summary>
            /// <param name="s">The hex string.</param>
            /// <param name="bytes">A byte array.</param>
            /// <returns>True if the hex string was successfully parsed.</returns>
            public static bool TryParseHexString(
                string s,
                out byte[] bytes
            )
            {
                if (s != null)
                {
                    if ((s.Length & 1) != 0) { s = "0" + s; } // make length of s even
                    bytes = new byte[s.Length / 2];
                    for (int i = 0; i < bytes.Length; i++)
                    {
                        string hex = s.Substring(2 * i, 2);
                        try
                        {
                            byte b = Convert.ToByte(hex, 16);
                            bytes[i] = b;
                        }
                        catch (FormatException)
                        {
                            bytes = null;
                            return false;
                        }
                    }
                    return true;
                }

                bytes = null;
                return false;
            }

            /// <summary>
            /// Tries to parse a hex string to a byte array.
            /// </summary>
            /// <param name="s">The hex string.</param>
            /// <param name="bytes">A byte array.</param>
            /// <returns>True if the hex string was successfully parsed.</returns>
            public static byte[] ParseHexString(string s)
            {
                byte[] bytes;
                if (!TryParseHexString(s, out bytes))
                {
                    var message = string.Format("'{0}' is not a valid hex string.", s);
                    throw new FormatException(message);
                }
                return bytes;
            }

            /// <summary>
            /// Converts a byte array to a hex string.
            /// </summary>
            /// <param name="bytes">The byte array.</param>
            /// <returns>A hex string.</returns>
            public static string ToHexString(
                byte[] bytes
            )
            {
                var sb = new StringBuilder(bytes.Length * 2);
                foreach (var b in bytes)
                {
                    sb.AppendFormat("{0:x2}", b);
                }
                return sb.ToString();
            }
        }

        internal static class Constants
        {
            #region private static fields
            private static readonly long dateTimeMaxValueMillisecondsSinceEpoch;
            private static readonly long dateTimeMinValueMillisecondsSinceEpoch;
            private static readonly DateTime unixEpoch;
            #endregion

            #region static constructor
            static Constants()
            {
                // unixEpoch has to be initialized first
                unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - unixEpoch).Ticks / 10000;
                dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - unixEpoch).Ticks / 10000;
            }
            #endregion

            #region public static properties
            /// <summary>
            /// Gets the number of milliseconds since the Unix epoch for DateTime.MaxValue.
            /// </summary>
            public static long DateTimeMaxValueMillisecondsSinceEpoch
            {
                get { return dateTimeMaxValueMillisecondsSinceEpoch; }
            }

            /// <summary>
            /// Gets the number of milliseconds since the Unix epoch for DateTime.MinValue.
            /// </summary>
            public static long DateTimeMinValueMillisecondsSinceEpoch
            {
                get { return dateTimeMinValueMillisecondsSinceEpoch; }
            }

            /// <summary>
            /// Gets the Unix Epoch for BSON DateTimes (1970-01-01).
            /// </summary>
            public static DateTime UnixEpoch { get { return unixEpoch; } }
            #endregion
        }

    }
}
原文地址:https://www.cnblogs.com/wuhuisheng/p/2471077.html