C# 在单件模式的时候该怎么及时销毁实例化的对象

会打代码的扫地王大爷 2015-08-13 05:04:38

using System;

namespace 单件模式
{
public class SingletonException:Exception
{
public SingletonException (string s):base (s)
{

}
}
public class Spooler
{
static bool instance_flag=false;
public Spooler()
{
if(instance_flag)
throw new SingletonException("已存在一个实例");
else
instance_flag=true;
Console.WriteLine("实例已打开");
}
~Spooler()
{
instance_flag=false;
}
}
class Program
{
public static void Main(string[] args)
{
Spooler pr1,pr2;
Console.WriteLine("打开一个实例");
try
{
pr1=new Spooler();
}
catch(SingletonException e)
{
Console.WriteLine(e.Message);
}

pr1=null;
GC.Collect();//这里似乎并没有什么用,请问有什么解决方案吗
Console.WriteLine("打开两个实例");
try
{
pr2=new Spooler();
}
catch(SingletonException e)
{
Console.WriteLine(e.Message);
}

Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
...全文
618 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
Poopaye 2015-08-14
  • 打赏
  • 举报
回复
引用 8 楼 wlmnzf 的回复:
我的意思是说,如果我想要创建第二个对象,要怎样先销毁之前的那个对象
别管第一个对象,让.net自己去回收
  • 打赏
  • 举报
回复
如果你写
static void Main(string[] args)
{
    var pr1 = new Spooler();
    pr1 = new Spooler();
    GC.Collect();
    Console.Write("Press any key to continue . . . ");
    Console.ReadKey(true);
}
你会看到,由于 pr1 引用了新的对象实例,那么在执行 GC.Collect() 时,第一个对象实例就被销毁了(销毁之前它的析构方法被执行了)。 当然实际的编程中是直接写成
static void Main(string[] args)
{
    var pr1 = new Spooler();
    pr1 = new Spooler();
    Console.Write("Press any key to continue . . . ");
    Console.ReadKey(true);
}
也就是说不要画蛇添足地去手动调用 GC.Collect() 方法(有些人硬要你自己手动去调用GC.Coolect()方法,你要自己判明是否真的必要)。因为GC会在必要时自动去执行Collect方法,而通常不需要牺牲性能去为了释放一点点(一个Spooler对象)的空间。
  • 打赏
  • 举报
回复
引用 8 楼 wlmnzf 的回复:
我的意思是说,如果我想要创建第二个对象,要怎样先销毁之前的那个对象
不需要考虑这种无用的事情。之前的那个对象过一会儿会自动被GC销毁,用不着你去操心。 这是.net,别忘记了。这不是c++。
  • 打赏
  • 举报
回复
假设你写代码
public class SingletonException : Exception
{
    public SingletonException(string s) : base(s)
    {

    }
}
public class Spooler
{
    public Spooler()
    {
        Console.WriteLine("实例已打开");
    }
    ~Spooler()
    {
        Console.WriteLine("%%%%%");
    }
}
class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 1000; ++i)
            t0();
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    private static void t0()
    {
        var pr1 = new Spooler();
        var pr2 = new Spooler();
        pr2 = null; //实际上写这个是多余的,pr1和pr2会同时被 GC 销毁
    }
}
那么运行时,你基本上看不到对象释放时的析构方法调用输出。 但是假设修改一下循环次数,例如
public class SingletonException : Exception
{
    public SingletonException(string s) : base(s)
    {

    }
}
public class Spooler
{
    public Spooler()
    {
        Console.WriteLine("实例已打开");
    }
    ~Spooler()
    {
        Console.WriteLine("%%%%%");
    }
}
class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 100000; ++i)
            t0();
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    private static void t0()
    {
        var pr1 = new Spooler();
        var pr2 = new Spooler();
        pr2 = null; //实际上写这个是多余的,pr1和pr2会同时被 GC 销毁。只要对象不用了,过一会儿自然就会被GC销毁。
    }
}
或者循环更多次,耐心等待10秒钟,你可能就能从打印结果中看到了析构方法被执行的日志了。 这是因为GC是自动的、智能的。它不会动不动就起动。如果动不动就启动,有些人人为这就可以“及时”释放空间了,实际上整个程序就慢了。只有必要时GC才会启动,并不会在无需启动时提前启动,例如这里就是这种情况。
  • 打赏
  • 举报
回复
引用 1 楼 shingoscar 的回复:
单例,也不是你这个写法 你现在是想保证什么?前一个Spooler没被回收前禁止再创建一个? 附单例:
class Singleton
{
	static Singleton _instance;
	
	public static Singleton Instance
	{
 		get
		{
			if (_instance == null)
				_instance = new Singleton();
			return _instance;
		}
	}

	Singleton()
	{
	}
}
我的意思是说,如果我想要创建第二个对象,要怎样先销毁之前的那个对象
winnowc 2015-08-13
  • 打赏
  • 举报
回复
说单例一般是整个程序生命周期下全局唯一的,你的用法就已经不能叫单例了,更像是互斥体 Mutex 的用法,不过这是用于线程同步的构造,如果需求不是线程同步,那么感觉更像Lazy<T>。可以构建一个MyLazy<T>的辅助类,类似Lazy<T>,提供接受工厂方法的构造器,Value属性,额外再提供一个Reset()方法。 Value属性第一次被访问时,使用工厂方法创建T的实例,之后Value都返回同一实例,直到调用Reset(),把当前实例抛弃。如果非要使用异常来判断(这不是个好方式),那么把Value属性换成Acquire()方法,第一次Acquire()的时候创建并返回实例,之后调用扔异常,直到被Reset()。 另外,不要滥用析构方法,这会导致GC效率下降。一般的原则是如果没有直接/间接使用非托管对象,那么不应该使用析构方法。作用域的控制应该自己实现,不要依赖不确定的GC。
  • 打赏
  • 举报
回复
或者可以再改一下
public class SingletonException : Exception
{
    public SingletonException(string s) : base(s)
    {

    }
}
public class Spooler
{
    public Spooler()
    {
        Console.WriteLine("实例已打开");
    }
    ~Spooler()
    {
        Console.WriteLine("%%%%%");
    }
}
class Program
{
    static void Main(string[] args)
    {
        t1();
        var pr2 = new Spooler();
        pr2 = null;
        GC.Collect();
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    private static void t1()
    {
        var pr1 = new Spooler();
    }
}
这样可以清楚地看到析构方法执行了。
  • 打赏
  • 举报
回复
你这个显然不是什么 singleton,你都 new 两个对象实例了! 至于 GC.Collect 的问题,这样写
public class SingletonException : Exception
{
    public SingletonException(string s) : base(s)
    {

    }
}
public class Spooler
{
    public Spooler()
    {
        Console.WriteLine("实例已打开");
    }
    ~Spooler()
    {
        Console.WriteLine("%%%%%");
    }
}
class Program
{
    static void Main(string[] args)
    {
        t1();
        GC.Collect();
        var pr2 = new Spooler();
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    private static void t1()
    {
        var pr1 = new Spooler();
    }
}
  • 打赏
  • 举报
回复
你这个是单例,瞎了……
  • 打赏
  • 举报
回复
而且你那个单例是有问题的,不严密
  • 打赏
  • 举报
回复
单例是相对业务逻辑来说的,并不影响垃圾回收器工作。 因为GC只要发现你这个对象,没有任何引用了,它就会回收(不一定是实时的)GC才不管你的对象是不是什么单例,也不需要它管
Poopaye 2015-08-13
  • 打赏
  • 举报
回复
单例,也不是你这个写法 你现在是想保证什么?前一个Spooler没被回收前禁止再创建一个? 附单例:
class Singleton
{
	static Singleton _instance;
	
	public static Singleton Instance
	{
 		get
		{
			if (_instance == null)
				_instance = new Singleton();
			return _instance;
		}
	}

	Singleton()
	{
	}
}

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧