Imeplement C# Singleton Class

Context

You are building an application in C#. You need a class that has only one instance, and you need to provide a global point of access to the instance. You want to be sure that your solution is efficient and that it takes advantage of the Microsoft .NET common language runtime features. You may also want to make sure that your solution is thread safe.

Implementation Strategy

Even though Singleton is a comparatively simple pattern, there are various tradeoffs and options, depending upon the implementation. The following is a series of implementation strategies with a discussion of their strengths and weaknesses.

Singleton

The following implementation of the Singleton design pattern follows the solution presented in Design Patterns: Elements of Reusable Object-Oriented Software [Gamma95] but modifies it to take advantage of language features available in C#, such as properties:

 

using System;

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}
 

This implementation has two main advantages:

  • Because the instance is created inside the Instance property method, the class can exercise additional functionality (for example, instantiating a subclass), even though it may introduce unwelcome dependencies.

  • The instantiation is not performed until an object asks for an instance; this approach is referred to as lazy instantiation. Lazy instantiation avoids instantiating unnecessary singletons when the application starts.

    The main disadvantage of this implementation, however, is that it is not safe for multithreaded environments. If separate threads of execution enter the Instance property method at the same time, more that one instance of the Singleton object may be created. Each thread could execute the following statement and decide that a new instance has to be created:

    if (instance == null)

    Various approaches solve this problem. One approach is to use an idiom referred to as Double-Check Locking [Lea99]. However, C# in combination with the common language runtime provides a static initialization approach, which circumvents these issues without requiring the developer to explicitly code for thread safety.

    Static Initialization

    One of the reasons Design Patterns [Gamma95] avoided static initialization is because the C++ specification left some ambiguity around the initialization order of static variables. Fortunately, the .NET Framework resolves this ambiguity through its handling of variable initialization:

     
    
    public sealed class Singleton
    {
       private static readonly Singleton instance = new Singleton();
       
       private Singleton(){}
    
       public static Singleton Instance
       {
          get 
          {
             return instance; 
          }
       }
    }
     

    In this strategy, the instance is created the first time any member of the class is referenced. The common language runtime takes care of the variable initialization. The class is marked sealed to prevent derivation, which could add instances. For a discussion of the pros and cons of marking a class sealed, see [Sells03]. In addition, the variable is marked readonly, which means that it can be assigned only during static initialization (which is shown here) or in a class constructor.

    This implementation is similar to the preceding example, except that it relies on the common language runtime to initialize the variable. It still addresses the two basic problems that the Singleton pattern is trying to solve: global access and instantiation control. The public static property provides a global access point to the instance. Also, because the constructor is private, the Singleton class cannot be instantiated outside of the class itself; therefore, the variable refers to the only instance that can exist in the system.

    Because the Singleton instance is referenced by a private static member variable, the instantiation does not occur until the class is first referenced by a call to the Instance property. This solution therefore implements a form of the lazy instantiation property, as in the Design Patterns form of Singleton.

    The only potential downside of this approach is that you have less control over the mechanics of the instantiation. In the Design Patterns form, you were able to use a nondefault constructor or perform other tasks before the instantiation. Because the .NET Framework performs the initialization in this solution, you do not have these options. In most cases, static initialization is the preferred approach for implementing a Singleton in .NET.

    Multithreaded Singleton

    Static initialization is suitable for most situations. When your application must delay the instantiation, use a non-default constructor or perform other tasks before the instantiation, and work in a multithreaded environment, you need a different solution. Cases do exist, however, in which you cannot rely on the common language runtime to ensure thread safety, as in the Static Initialization example. In such cases, you must use specific language capabilities to ensure that only one instance of the object is created in the presence of multiple threads. One of the more common solutions is to use the Double-Check Locking [Lea99] idiom to keep separate threads from creating new instances of the singleton at the same time.

    Note: The common language runtime resolves issues related to using Double-Check Locking that are common in other environments. For more information about these issues, see "The ‘Double-Checked Locking Is Broken’ Declaration," on the University of Maryland, Department of Computer Science Web site, at http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html.

    The following implementation allows only a single thread to enter the critical area, which the lock block identifies, when no instance of Singleton has yet been created:

     
    
    using System;
    
    public sealed class Singleton
    {
       private static volatile Singleton instance;
       private static object syncRoot = new Object();
    
       private Singleton() {}
    
       public static Singleton Instance
       {
          get 
          {
             if (instance == null) 
             {
                lock (syncRoot) 
                {
                   if (instance == null) 
                      instance = new Singleton();
                }
             }
    
             return instance;
          }
       }
    }
     

    This approach ensures that only one instance is created and only when the instance is needed. Also, the variable is declared to be volatile to ensure that assignment to the instance variable completes before the instance variable can be accessed. Lastly, this approach uses a syncRoot instance to lock on, rather than locking on the type itself, to avoid deadlocks.

    This double-check locking approach solves the thread concurrency problems while avoiding an exclusive lock in every call to the Instance property method. It also allows you to delay instantiation until the object is first accessed. In practice, an application rarely requires this type of implementation. In most cases, the static initialization approach is sufficient.

    Resulting Context

    Implementing Singleton in C# results in the following benefits and liabilities:

    Benefits
  • The static initialization approach is possible because the .NET Framework explicitly defines how and when static variable initialization occurs.

  • The Double-Check Locking idiom described earlier in "Multithreaded Singleton" is implemented correctly in the common language runtime.

    Liabilities

    If your multithreaded application requires explicit initialization, you have to take precautions to avoid threading issues.

  • C#网络编程7

    使用无连接套接字,由于SOCK_DGRAM类型的套接字使用UDP协议,所以不需要在网络设备之间发送连接信息。因此,经常很难确定那个事“服务器”,那个事“客户机”,如果一个设备最初是在等待远程设备的信息,那套接字就必须使用bind()函数绑定到一个本地地址/端口对上。

    服务器端函数如下:

    socket(),bind(),recvfrom(),sendto(),close()

    客户端函数如下:

    socket(),              sendto(),   recvfrom(),close()

    可以像TCP一样,UDP也可以使用shutdown()和close()函数。

    由于标准的UNIX网络编程模式有一个缺点,就是如果I/O函数不能立即处理,I/O函数就会阻塞。这样,就对于要连续处理其他事件的程序引起问题,使用无阻赛套接字或使用多路套接字技术。

    无阻塞套接字,使用fcntl()函数格式:

    int fcntl(int fd,int cmd,int arg)

    C#中Hash函数的用法

    用例程序如下:

    using System;
    namespace HashTable
    {
        class Program
        {
            static void Main(string[] args)
            {
                Hashtable myHT1 = new Hashtable();
                myHT1.Add("first", "Hello");
                myHT1.Add("Alarm","12:12:12");
                myHT1.Add("third", "!");
                myHT1.Add(12, "12:23:23");

                foreach (DictionaryEntry de in myHT1)
                {
                    Console.WriteLine("\t{0}:\t{1}", de.Key, de.Value);

                }
            }
        }
    }

    输出结果:

    first:  Hello
    third:  !
    Alarm:  12:12:12
    12:     12:23:23

    C#网络编程6

    使用面向连接的套接字,IP连接领域不外乎两种通信方式,面向连接的和无连接的。在面向连接的套接字中,使用SOCK_STREAM类型,使用TCP协议来建立两个端点之间的会话。

    服务器端函数:

    对于服务器程序,建立连接的套接字必须绑定到用于TCP通信的本地IP地址和端口号上。可以用UNIX的bind()函数来完成这项工作:

    int bind( SOCKET s, const struct sockaddr FAR *name, int namelen );

    在bind函数中,第一个参数引用socket()函数返回的值。addr参数引用一个sockaddr地址/端口对,以定义本地网络连接。由于服务器通常接受再它自己IP地址上的链接,所以这是本地设备的IP地址和为应用程序分配的TCP端口。如果不知道本地系统的IP地址,可以使用INADDR_ANY值,允许套接字绑定在系统的任何一个本地地址上。在套接字绑定到一个地址和端口后,服务器程序必须准备接受从远程客户机的连接。这个过程分两步完成:

    a)程序寻找一个进入的连接

    b)发送和接受数据

    服务器程序必须首先用listen()监听网络,寻找一个进入的连接。下一步,它必须用accept()函数接受客户端的连接尝试。listen()函数如下:

    int listen( SOCKET s, int backlog );

    第一个参数引用socket()函数建立的套接字描述符。backlog参数引用系统可接受的正在等待进行的连接数,相当于最大连接数的意思。还有accept()函数,recv()函数,send()函数,close()函数。

    客户端函数:

    connect()函数绑定到特定的主机地址和应用程序端口。send()函数和recv()函数,以及关闭连接的两个函数shutdown()和close()。当客户机和服务器结束数据传送之后,应当用两个命令正确的结束连接,close()函数有可能单独的使用。不过如果要温和一点则先用shutdown()函数。

    int shutdown( SOCKET s, int how );

    然后再用close()函数。shutdown的第二参数how可以决定怎么关闭连接。

    0    表示不再可以接受包

    1    表示不再可以发送包

    2    表示不再可以发送和接受包

    C#网络编程5

    最后一种确定系统IP的信息的方法是利用C#DNS(域名系统)类。程序DNSName.cs清楚地说明这一方法。

    using System;
    using System.Net;
    class DNSName
    {
        public static void Main()
        {
            string hostName = Dns.GetHostName();
            Console.WriteLine("Local hostname:{0}",hostName);
            IPHostEntry myself = Dns.GetHostEntry(hostName);
            foreach (IPAddress address in myself.AddressList)
            {
                Console.WriteLine("IP Address:{0}",address.ToString());
            }
        }
    }

    运行结果:

    Local hostname:WWW-4BC528D5D3C
    IP Address:::1
    IP Address:192.168.100.99
    IP Address:211.86.159.176

    C#网络编程4

    用c#查询WMI,找到系统中安装的每一个网络接口,并显示返回的IP信息,步骤如下:

    1.为查询数据库表,创建一个SQL选择语句的ManagementObjectSearcher对象。

    2.创建一个ManagementObjectCollection对象,执行SQL查询来获得结果集。

    3.在foreach循环中,给ManagementObjectCollection中的每一个对象分配一个新的ManagementObject。

    4.给合格的类型变量分配想要的数据字段。

    程序如WMICardGrab.cs。该程序用C#的WMI类获得网络接口的IP信息。

    using System;
    using System.Management;
    class WMICardGrab
    {
        public static void Main()
        {
            ManagementObjectSearcher query = new ManagementObjectSearcher("select * from Win32_NetworkAdapterConfiguration Where IPEnabled=’TRUE’");
            //每一个集合项表示结果集中的一条记录,每条记录表示一个网络接口。
            ManagementObjectCollection queryCollection = query.Get();
            foreach (ManagementObject mo in queryCollection)
            {
                string[] addresses = (string[])mo["IPAddress"];
                string[] subnets = (string[])mo["IPSubnet"];
                string[] defaultgateways = (string[])mo["DefaultIPGateway"];
                Console.WriteLine("Network Card :{0}",mo["Description"]);
                Console.WriteLine("MAC Address :{0}", mo["MACAdress"]);
                foreach (string ipaddress in addresses)
                {
                    Console.WriteLine("    IP Address:{0}",ipaddress);
                }
                foreach (string subnet in subnets)
                {
                    Console.WriteLine("    Subnet Mask:{0}", subnet);
                }
                foreach (string defaultgateway in defaultgateways)
                {
                    Console.WriteLine("    Gateway:{0}", defaultgateway);
                }
            }
        }
    }

    运行结果:

    Network Card :WAN (PPP/SLIP) Interface

    未处理的异常:  System.Management.ManagementException: 找不到
       在 System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
       在 System.Management.PropertyData.RefreshPropertyInfo()
       在 System.Management.PropertyDataCollection.get_Item(String propertyName)
       在 System.Management.ManagementBaseObject.GetPropertyValue(String propertyName)
       在 System.Management.ManagementBaseObject.get_Item(String propertyName)
       在 WMICardGrab.Main()

    C#网络编程3

    WMI是英文Windows Management Instrumentation的简写,它的功能主要是:访问本地主机的一些信息和服务,可以管理远程计算机(当然你必须要拥有足够的权限),比如:重启,关机,关闭进程,创建进程等。


    2、 如何用WMI获得本地磁盘的信息?
    首先要在VS.NET中创建一个项目,然后在添加引用中引用一个.net的装配件:System.Management.dll,这样你的项目才能使用WMI。代码如下:

    using System;
    using System.Management;

    class Sample_ManagementObject
    {
        public static int Main(string[] args)
        {
            SelectQuery query = new SelectQuery("Select * From Win32_LogicalDisk");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
            foreach (ManagementBaseObject disk in searcher.Get())
            {
                Console.WriteLine("\r" + disk["Name"] + " " + disk["DriveType"] + " " + disk["VolumeName"]);
            }
            Console.ReadLine();
            return 0;
        }
    }

    输出结果:

    C: 3
    D: 3 SOFTWARE
    E: 3 MEDIA
    F: 3 BACKUP
    G: 5
    H: 5

    3、在第二点的基础上稍微修改。获取C盘的信息。

           ManagementObject disk = new ManagementObject("win32_logicaldisk.deviceid=\"c:\"");
           disk.Get();
           Console.WriteLine("Logical Disk Size = " + disk["Size"] + " bytes");

    输出结果:

    Logical Disk Size = 16853565440 bytes

    4、如何列出机器中所有的共享资源?

            ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_share");
            foreach (ManagementObject share in searcher.Get())
            {
                Console.WriteLine(share.GetText(TextFormat.Mof));
            }

    输出结果:

    instance of Win32_Share
    {
            AllowMaximum = TRUE;
            Caption = "远程 IPC";
            Description = "远程 IPC";
            Name = "IPC$";
            Path = "";
            Status = "OK";
            Type = 2147483651;
    };

    5、怎样写程控制让系统中的某个文件夹共享或取消共享?

        public static void Main(string[] args)
        {
            ManagementClass _class = new ManagementClass(new ManagementPath("Win32_Share"));

            object[] obj = { "C:\\Temp", "我的共享", 0, 10, "Dot Net 实现的共享", "" };

            _class.InvokeMethod("create", obj);
        }

    如果将
    object[] obj = {"C:\\Temp","我的共享",0,10,"Dot Net 实现的共享",""};
    改为
    object[] obj = {"C:\\Temp","我的共享",0,null,"Dot Net 实现的共享",""};
    就可以实现授权给最多用户了。

    6、通过WMI修改IP,而实现不用重新启动?

    using System;
    using System.Management;
    using System.Threading;

    namespace WmiIpChanger
    {
        class IpChanger
        {
            [MTAThread]
            static void Main(string[] args)
            {
                ReportIP();
                // SwitchToDHCP();
                SwitchToStatic();
                Thread.Sleep(5000);
                ReportIP();
                Console.WriteLine("end.");
            }

            static void SwitchToDHCP()
            {
                ManagementBaseObject inPar = null;
                ManagementBaseObject outPar = null;
                ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if (!(bool)mo["IPEnabled"])
                        continue;

                    inPar = mo.GetMethodParameters("EnableDHCP");
                    outPar = mo.InvokeMethod("EnableDHCP", inPar, null);
                    break;
                }
            }

            static void SwitchToStatic()
            {
                ManagementBaseObject inPar = null;
                ManagementBaseObject outPar = null;
                ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if (!(bool)mo["IPEnabled"])
                        continue;

                    inPar = mo.GetMethodParameters("EnableStatic");
                    inPar["IPAddress"] = new string[] { "192.168.1.1" };
                    inPar["SubnetMask"] = new string[] { "255.255.255.0" };
                    outPar = mo.InvokeMethod("EnableStatic", inPar, null);
                    break;
                }
            }

            static void ReportIP()
            {
                Console.WriteLine("****** Current IP addresses:");
                ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if (!(bool)mo["IPEnabled"])
                        continue;

                    Console.WriteLine("{0}\n SVC: ‘{1}’ MAC: [{2}]", (string)mo["Caption"],
                    (string)mo["ServiceName"], (string)mo["MACAddress"]);

                    string[] addresses = (string[])mo["IPAddress"];
                    string[] subnets = (string[])mo["IPSubnet"];

                    Console.WriteLine(" Addresses :");
                    foreach (string sad in addresses)
                        Console.WriteLine("\t’{0}’", sad);

                    Console.WriteLine(" Subnets :");
                    foreach (string sub in subnets)
                        Console.WriteLine("\t’{0}’", sub);
                }
            }
        }
    }

    运行结果:

    ****** Current IP addresses:
    [00393219] WAN 微型端口 (IP)
    SVC: ‘NdisWan’ MAC: [00:53:45:00:00:00]
    Addresses :
            ‘211.86.159.90’
    Subnets :
            ‘255.255.255.255’
    [00000008] Broadcom NetLink (TM) Gigabit Ethernet
    SVC: ‘k57w2k’ MAC: [00:26:9E:14:05:43]
    Addresses :
            ‘192.168.100.99’
    Subnets :
            ‘255.255.255.0’
    [00000010] Intel(R) Wireless WiFi Link 5100
    SVC: ‘NETw5x32’ MAC: [00:1E:65:56:4E:3E]
    Addresses :
            ‘0.0.0.0’
    Subnets :
            ”
    ****** Current IP addresses:
    [00393219] WAN 微型端口 (IP)
    SVC: ‘NdisWan’ MAC: [00:53:45:00:00:00]
    Addresses :
            ‘211.86.159.90’
    Subnets :
            ‘255.255.255.255’
    [00000008] Broadcom NetLink (TM) Gigabit Ethernet
    SVC: ‘k57w2k’ MAC: [00:26:9E:14:05:43]
    Addresses :
            ‘192.168.100.99’
    Subnets :
            ‘255.255.255.0’
    [00000010] Intel(R) Wireless WiFi Link 5100
    SVC: ‘NETw5x32’ MAC: [00:1E:65:56:4E:3E]
    Addresses :
            ‘0.0.0.0’
    Subnets :
            ”
    end.

    8、如何利用WMI远程重启远程计算机?

    见C#fromMSDN文件夹下WMI的ReStartSYS.cs文件。

    9、利用WMI创建一个新的进程?

    见C#fromMSDN文件夹下WMI的CreateNewProcess.cs文件.

    10、如何通过WMI终止一个进程?

    见C#fromMSDN文件夹下WMI的EndProcess.cs文件.

    11、一个使用WMI后的异常处理的问题

    见C#fromMSDN文件夹下WMI的ExceptionWMI.cs文件.

    注:我的个人空间里面C#文件夹中有其他的WMI相关应用程序。例如:Other.cs, ListAllNetworkMessage.cs