接口不通过引用

时间:2015-05-22 13:11:58

标签: c# asp.net pointers reference

我需要在运行时更改接口的实现。许多类都引用了这个接口。

这是我的测试用例,它不像我期望的那样工作。 (更改界面的引用似乎不会在其使用的所有位置更新)

以下是示例:

// interface to change at runtime
interface IDiet
{
    void Eat();
}

class CarnivoreDiet : IDiet
{
    public void Eat()
    {
        Debug.WriteLine("Eat chicken");
    }
}

class HerbivoreDiet : IDiet
{
    public void Eat()
    {
        Debug.WriteLine("Eat spinach");
    }
}

class Human : IDiet
{
    private readonly IDiet _diet;
    public Human(IDiet diet)
    {
        _diet = diet;
    }

    public void Eat()
    {
        _diet.Eat();
    }
}

void Main()
{
    IDiet diet = new CarnivoreDiet();
    Human human = new Human(diet);
    human.Eat();
    // outputs "Eat chicken"

    diet = new HerbivoreDiet();
    human.Eat();
    // still outputs "Eat chicken" even if i changed the reference of IDiet interface
}

为什么IDiet实例中的Human接口没有更新?

PS:IDiet接口在很多类中使用,因此添加像SetDiet(IDiet diet)这样的方法不会成为解决方案。

5 个答案:

答案 0 :(得分:7)

当您通过此代码传递对象引用时:

Human human = new Human(diet);

将引用(对象的地址)复制到其参数:

public Human(IDiet diet)
{
        _diet = diet;
}

它们是 3个不同的内存块,包含对象的相同引用:原始diet,参数变量(diet)和您的类属性({{ 1}})。

所以当你执行你的代码时:

_diet

此内存块现在包含对新对象(diet = new HerbivoreDiet(); )的引用,但HerbivoreDiet内的引用仍引用旧对象。

答案 1 :(得分:1)

diet是一个局部变量,它的值是指向内存中实际对象的指针(地址)。

如果你去diet.SomeProperty = "foo",你就改变了内存中的对象,这个对象随处可见。

如果你说diet = new Diet(),你用一个指向另一个对象的新指针覆盖局部变量diet

Human引用未更改,因为指针是按值传递的。他们指向的对象是相同的(直到你覆盖饮食),但指针是彼此的副本。

你应该让饮食成为一种无障碍的,非阅读的财产并改变它。或者创造一个新的人类。

答案 2 :(得分:1)

由于您已经实现了一个外观,为什么不实现第二个外观:

Human

构造其中一个并将 it 传递给void Main() { IDiet diet = new CarnivoreDiet(); var changable = new ChangableDiet(diet) Human human = new Human(changable ); human.Eat(); // outputs "Eat chicken" changable.Diet = new HerbivoreDiet(); human.Eat(); // outputs "Eat spinach" } 构造函数。

(1..5).each do |ind|
  ThinkingSphinx::Index.define :incident, name: "incident_index_#{ind}" ... do
    where "incidents.id % 5 = #{ind - 1}"
    indexes ...
    has ...
end

答案 3 :(得分:1)

问题:为什么IDiet接口不在Human实例内更新?

只是因为您的界面引用按值传递,这意味着您在Human类中保留的引用是指向同一IDiet

的副本

稍后再做

diet = new HerbivoreDiet();

您只需更改Human类之外的引用,该引用仍然是自己的副本,因此无法更改其引用。

以下是您可以实施的解决方案:

public interface IProvider<T>
{
  public T Current {get; set;}
}

public class DietProvider : IProvider<IDiet>
{
  public IDiet Current {get; set;}
}

class Human : IDiet
{
  private readonly IProvider<IDiet> _dietProvider;

  public Human(IProvider<IDiet> dietProvider)
  {
    _dietProvider = dietProvider;
  }

  public void Eat()
  {
    _dietProvider.Current.Eat();
  }
}

void Main()
{
  IProvider<IDiet> dietProvider= new DietProvider { Current = new CarnivoreDiet()};
  Human human = new Human(dietProvider);
  human.Eat();
  // outputs "Eat chicken"

  dietProvider.Current = new HerbivoreDiet();
  human.Eat();
}

答案 4 :(得分:0)

这与接口无关。 您的代码所做的是通过值将变量传递给类构造函数,尽管该值是对实例的引用。然后,将此值复制到新创建的对象的实例的成员。然后,当您更改最初用于保存值的变量中的值时,它对您类的实例中的内部成员没有任何影响。

int完全相同。请考虑以下示例:

class MyClass {
   private int mInt;
   public MyClass(int theInt) {
      mInt = theInt;
   }

   public int GetTheInt() { return mInt; }
}

void Main()
{
   int anInt = 5;
   MyClass MyInstance(anInt);
   anInt = 10;
   if(MyInstance.GetTheInt() == anInt)
      // Will never happen
   ;
}