使用单例模式时遇到的奇怪问题

后八十生人 2013-01-26 05:10:21
如下代码示例一个单例模式的类

public class ServiceManager
{
private static readonly ServiceManager Instance = new ServiceManager();

private ServiceManager() { }

public static ServiceManager Current
{
get
{
return ServiceManager.Instance;
}
}
}

public class B
{
public B()
{
var Current=ServiceManager.Current;
}
}


如果我通过 var B=new B() 创建 B 实例的话一切正常,ServiceManager只有一个实例

但如果我通过 Assembly.CreateInstance(typeof(B).FullName) 创建 B 实例的话就会和预期的不一样,ServiceManager的私有构造函数还会被调用一次……

这是什么问题?该如何解决呢?
...全文
855 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
Lakers8888 2013-01-28
  • 打赏
  • 举报
回复
我没有分呀,我要提问哈。
joyhen 2013-01-28
  • 打赏
  • 举报
回复
类的静态变量会先于静态构造函数初始化的
joyhen 2013-01-28
  • 打赏
  • 举报
回复
比如类BudgetClass:

 public partial class BudgetClass
    {

        #region GetInstance
        private static readonly object LockHelper = new object();
        private static BudgetClass _instance;
        public static BudgetClass Instance
        {
            get
            {
                lock (LockHelper)
                {
                    return _instance ?? (_instance = new BudgetClass());
                }
            }

        }

......
}
裸奔在上海 2013-01-28
  • 打赏
  • 举报
回复
引用 23 楼 btman52 的回复:
引用 22 楼 happybabyq 的回复:问题已完美解决! 能否共享解决方法?
是把两个工程合并为一个还是修改了加载方法?
btman52 2013-01-28
  • 打赏
  • 举报
回复
引用 22 楼 happybabyq 的回复:
问题已完美解决!
能否共享解决方法?
后八十生人 2013-01-28
  • 打赏
  • 举报
回复
问题已完美解决!
玲cc 2013-01-28
  • 打赏
  • 举报
回复
为什么需要在A中创建一个B的实例呢?如果B是一个服务提供类,是否可以通过接口对外暴露相应的功能?这样在A中就不需要显示实例化B了。
玲cc 2013-01-27
  • 打赏
  • 举报
回复
好吧。我觉得事情的真相应该是这样的:你应该有两个工程,一个是主工程A和另一个副工程A1(也就是你的类B和ServiceManager所在的工程)。现在你是不是想在A中加载A1的dll,动态的创建类B?如果是这样,你是不是在工程A的Reference中加入了工程A1?如果还是这样,你是不是还在A中某处用Assembly.LoadFile来加载了A1的dll。如果是这样,那你应该会遇到你提到的问题。 如果上面的场景描述正确,lz得到7楼异常的原因是因为调用了下面这段代码吧?

Assembly a1 = Assembly.LoadFile(path);//The path of A1.dll
B b = (B)a1.CreateInstance(typeof(B).FullName);
玲cc 2013-01-27
  • 打赏
  • 举报
回复
你这是一个基于插件的程序吧?你那个有冲突的dll是不是被load了两次?
后八十生人 2013-01-27
  • 打赏
  • 举报
回复
引用 18 楼 sp1234 的回复:
Assembly.Load稍微复杂,它有好多种形式。你应该选择正确的形式——内部调用LoadFromAssemblyName——的形式。而你的形式,很明显地是另外加载一个Assembly了! 可能有人没有想到内存中会有重名的Assembly、重名的Type,所以没有想到需要深入去了解Load。
你所说的“很明显地是另外加载一个Assembly了”具体如何理解? 我这里具体的情况是这样的: 应用程序使用Assembly.Load(Byte[])的方式从内存中加载了一个程序集,然后将其中的一个类型实例化,并调用这个实例中的方法(将此处表示为A) 这个方法中实现一个ServiceHost,并将这个程序集中的另外一个类型B作为服务运行。 问题就在这里,在A的上下文中创建一个B实例,与ServiceHost中B的实例做类型对比,得到的结果是不一样的类型. 我猜想,应该内存中出现了同一程序集的两个副本,或者是有资料中描述的创建实例时应用了不同的上下文导致了实例类型不同。 上述问题通过Assembly.Load(AssemblyName)就可以解决,但是否有可能在不改变加载方式(从内存中加载)的情况下解决此问题呢?
shuyisheng 2013-01-27
  • 打赏
  • 举报
回复
.NET深入编程学习了。
  • 打赏
  • 举报
回复
Assembly.Load稍微复杂,它有好多种形式。你应该选择正确的形式——内部调用LoadFromAssemblyName——的形式。而你的形式,很明显地是另外加载一个Assembly了! 可能有人没有想到内存中会有重名的Assembly、重名的Type,所以没有想到需要深入去了解Load。
salecn 2013-01-27
  • 打赏
  • 举报
回复
来好好学习下!
玲cc 2013-01-27
  • 打赏
  • 举报
回复
lz你在这里可能有一个误区。用我之前的工程A和工程A1的例子继续说明。如果我没猜错,你在A中引用A1是为了使用类B,这实际上是在编译时就决定了工程A对于类B的依赖。但是从一个标准的组件系统来说,工程A在编译时并不能对具体的类B依赖,而是应该对类B的抽象产生依赖。 程序集A

Assembly a1 = Assembly.LoadFile(path);//path is the filepath of A1.dll
BaseB b = (BaseB)a1.CreateInstance(typeName);//typeName is the name of class B
程序集A1

public class B:BaseB{}
程序集A2

public abstract BaseB{}
程序集A和程序集A1都引用了程序集A2。这样你在A中就可以去掉对程序集A1的引用,从而就不会出现你现在的问题。 当然还有一点要注意,在A中调用Assembly.Load方法加载包含有ViewModel的dll时,一定要保证只加载一次哦,否则View在寻找绑定的ViewModel时会出现问题。
  • 打赏
  • 举报
回复
引用 6 楼 happybabyq 的回复:
我发现关键问题不是单例类的代码有问题,而是这个程序集是使用Assembly.Load(Byte[])方法动态加载的, 我使用Assembly.CreateInstance(typeof(B)).GetType.Assembly 获得的程序集与 typeof(B).Assembly 获得的程序集不一样(虽然我使用强名称,并且这两个程序集的Fullname看上去都一样),这……
使用LoadFrom,不要使用LoadFile。 如果你使用LoadFile,那么它就是两个重名的Assembly,两套重名的Type!
后八十生人 2013-01-27
  • 打赏
  • 举报
回复
引用 9 楼 moonwrite 的回复:
private ServiceManager()//每次都会调用 private static ServiceManager()//只调用一次 如楼上各位所说的,你的单例模式写错了~static的才对~
你错了!
后八十生人 2013-01-27
  • 打赏
  • 举报
回复
引用 11 楼 scliu1987 的回复:
好吧。我觉得事情的真相应该是这样的:你应该有两个工程,一个是主工程A和另一个副工程A1(也就是你的类B和ServiceManager所在的工程)。现在你是不是想在A中加载A1的dll,动态的创建类B?如果是这样,你是不是在工程A的Reference中加入了工程A1?如果还是这样,你是不是还在A中某处用Assembly.LoadFile来加载了A1的dll。如果是这样,那你……
的确是这个问题,程序在启动时显示的加载了程序集(Assembly.Load(Byte[])方式),这样就导致了上述问题。 虽然我找到了两个解决方案: 1:用另外一个AppDomain承载这些程序集 2:将程序集放置在程序根目录或者GAC中,然后通过Assembly.Load(AssemblyName)方式加载 但这两个方案我暂时都不敢尝试: 第一种方案虽然最优雅并且最安全,可以说是组件框架一般的实现模式,但我手上的程序是基于MVVM模式设计的WPF程序,其中运用了大量的DataTemplate,并且Model直接声明了WCF的DataContact,我不知道也没有找到相关资料可以保证跨域封送不会对上述内容有影响,现在也没有时间去尝试。 第二种方案可能会是我最终采用的,虽然它并不符合设计的初衷。 不知道有没有关于第一种方法的资料?如能提供,不胜感激!
moonwrite 2013-01-26
  • 打赏
  • 举报
回复
private ServiceManager()//每次都会调用 private static ServiceManager()//只调用一次 如楼上各位所说的,你的单例模式写错了~static的才对~
后八十生人 2013-01-26
  • 打赏
  • 举报
回复
继续顶一下!专家求解!
加载更多回复(7)
超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承:  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装:  封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性:  多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 5、String是最基本的数据类型吗?  基本数据类型包括byte、int、char、long、float、double、boolean和short。  java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 6、int 和 Integer 有什么区别  Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型 封装类 boolean Boolean char Character byte Byte short Short int Integer long Long float Float double Double  引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 7、String 和StringBuffer的区别  JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的候你就可以使用StringBuffer。典型地,你可以使用 StringBuffers来动态构造字符数据。 8、运行异常与一般异常有何异同?  异常表示程序运行过程中可能出现的非正常状态,运行异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行异常,但是并不要求必须声明抛出未被捕获的运行异常。 9、说出Servlet的生命周期,并说出Servlet和CGI的区别。  Servlet被服务器实例化后,容器运行其init方法,请求到达运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 10、说出ArrayList,Vector, LinkedList的存储性能和特性  ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据只需要记录本项的前后项即可,所以插入速度较快。 11、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Ses
JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型封装类 booleanBoolean charCharacter byteByte shortShort intInteger longLong floatFloat doubleDouble 引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 4、String 和StringBuffer的区别 JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 5、运行异常与一般异常有何异同? 异常表示程序运行过程中可能出现的非正常状态,运行异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行异常,但是并不要求必须声明抛出未被捕获的运行异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,请求到达运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 7、说出ArrayList,Vector, LinkedList的存储性能和特性 ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据只需要记录本项的前后项即可,所以插入速度较快。 8、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Session Bean、Entity Bean、Message Driven Bea

110,539

社区成员

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

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

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