【问题标题】:Object not being set to null对象未设置为 null
【发布时间】:2014-11-14 17:29:23
【问题描述】:

我在Universe 类中有以下方法:

public IWorld CreateWorld(string name) {

    //Validations and stuff...

    IWorld world = new World(name);
    worlds.Add(name, world);
    return world;
}

创建一个新的World 实例,并将其添加到Dictionary<string, IWorld>。然后,我继续返回添加到字典中的相同引用

到目前为止一切顺利。

在同一个Universe类中,我也有如下方法:

public void DestroyWorld(string name) {

   //Validations and stuff...

   IWorld world = worlds[name];
   worlds.Remove(name);
   world.Dispose();
   world = null; // <- Setting the object to null
}

这里我从字典中获取对象引用,处理它,然后将其设置为 null。

在这个项目之外,我有我的Main 类:

public static void Main(strig[] args) {
   IWorld world = Universe.Instance.CreateWorld("Solarius");
   Console.WriteLine(world.Age); //Prints out the world's age
   Universe.Instance.DestroyWorld(world.Name);
   Console.WriteLine(world.Age); //NullPointerException not being thrown! Prints out the same world's age
}

为什么会这样?如果我在Universe.DestroyWorld 方法中将引用设置为null,为什么我可以调用world.Age

存储在字典中的引用与我在 Main 类中操作的引用不一样吗?

【问题讨论】:

  • 我没有看到定义年龄的代码。可以提供一下吗?
  • 只是IWorld接口上的一个属性:int Age { get; }
  • 为什么 DestroyWorld 的代码采用字符串参数而不是 IWorld?
  • 我有一个采用IWorld 的方法的重载,但我真的需要一个字符串,因为它是字典的键

标签: c# .net dictionary reference


【解决方案1】:

您必须记住,当您将对象作为参数传递给方法时,您实际上传递了引用的副本。因此,在将对象传递给方法之后,您实际上有两个地方可以引用该对象。一个是您的原件,另一个是原件的副本。目前,您仅将副本设置为空。

要获得您想要的结果,您可以将对象而不是名称传递给方法。你必须使用ref 关键字来完成。这种方式不会复制参考,但该方法使用您的原始参考。因此,当您在方法中操作对象时,您实际上将通过原始引用来操作它。

ublic void DestroyWorld(ref IWorld world) {
    world.Dispose();
    world = null; // <- Setting the object to null
}

然后像这样使用它:

public static void Main(strig[] args) {
    IWorld world = Universe.Instance.CreateWorld("Solarius");
    Console.WriteLine(world.Age); //Prints out the world's age
    Universe.Instance.DestroyWorld(ref world);
    Console.WriteLine(world.Age); // NullReferenceException
}

我知道它与您的原始代码有很大不同,但它会按照您期望的方式工作。 我强烈推荐这篇关于c#参数传递的文章,特别是关于通过'ref'关键字传递引用类型的部分:

http://yoda.arachsys.com/csharp/parameters.html

您可以在这里找到以更直观的方式解释的相同机制:

http://rapidapplicationdevelopment.blogspot.co.uk/2007/01/parameter-passing-in-c.html

【讨论】:

    【解决方案2】:

    world 设置为null 不会将对象设置为null。它将对象引用设置为null。

    将“对象”设置为 null 的想法甚至没有意义。

    【讨论】:

      【解决方案3】:

      您将DestroyWorld 方法的局部变量设置为null。这并没有将整个应用程序中引用World 实例的所有其他地方的每个变量都设置为null,只是一个变量。事实上,DestroyWorld 方法不可能做任何将Main 中的变量设置为null 的事情。

      【讨论】:

      • 那么,我方法中的local variableMain 中的不同?这是否意味着我有两个不同的副本?
      • @MatiCicero 你有两个对象引用的副本,是的。两个副本都引用同一个对象。当您销毁一张写有您的家庭住址的纸时,它是否会销毁全世界所有其他写有您的家庭住址的纸?它会毁了你的房子吗?
      • 感谢您提供的真实示例。我想我现在理解正确了。
      【解决方案4】:

      您必须了解您使用的是对对象的引用,而不是对象本身。

      //this sets the someVar to a reference to a new object
      var someVar = new World(); 
      
      //this sets the *variable* to a NULL reference
      //but it doesn't do anything to the original object
      someVar = null;
      

      对象死亡的方式是通过垃圾收集器,简单来说,当该计数为 0 时,它会计算对一个对象(它位于内存中的地址)的 活动 引用的数量允许垃圾收集器在同一内存空间中分配新对象。

      【讨论】:

        【解决方案5】:
        public IWorld CreateWorld(string name) {
        IWorld world = new World(name);
        worlds.Add(name, world);
        return world;
        

        }

        在您创建世界的地方,您将 IWorld 的一个实例返回给代码

        但是当你删除世界时,你并没有归还它

        public void DestroyWorld(string name) {
        IWorld world = worlds[name]
        worlds.Remove[name];
        world.Dispose();
        world = null;
        

        }

        您设置为 null 的世界是函数范围内的世界,因此,或者您返回要设置为 null 的 Iworld,或者您在函数中传递 IWorld 对象并将其设置为空;

        【讨论】:

          猜你喜欢
          • 2016-12-25
          • 2019-03-08
          • 2014-09-02
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-15
          相关资源
          最近更新 更多