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

后八十生人 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的私有构造函数还会被调用一次……

这是什么问题?该如何解决呢?
...全文
864 28 打赏 收藏 转发到动态 举报
AI 作业
写回复
用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)

111,098

社区成员

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

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

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