接口不通过引用传递

Interfaces are not passed by 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)这样的方法将不是解决方案。

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

Human human = new Human(diet);

引用(对象的地址)被复制到它的参数:

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

它们是 3 个不同的内存块,包含对对象的相同引用:您的原始 diet、参数变量 (diet) 和您的class 属性 (_diet).

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

diet = new HerbivoreDiet();

此内存块现在包含对新对象 (HerbivoreDiet) 的引用,但 Human 中的内存块仍引用旧对象。

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

如果你去diet.SomeProperty = "foo",你改变了内存中的对象,到处都反映出来。

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

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

您应该将饮食设置为可访问的、非只读的 属性 并进行更改。或者创建一个新人类。

既然你已经实现了一个外观,为什么不实现第二个:

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

    public Diet Diet { get { return _diet;} set { _diet = value; } }

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

构建其中之一并将传递给Human构造函数。

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"
}

这与接口无关。 您的代码所做的是按值将变量传递给 class 构造函数,尽管该值是对实例的引用。然后,您将这个值复制到新创建的对象的实例成员中。然后,当您更改最初用于保存该值的变量中的值时,它对 class.

实例中的内部成员没有影响

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
   ;
}

问题:为什么 IDiet 接口在 Human 实例中没有更新?

仅仅因为你的接口引用是通过值传递的,这意味着你在Humanclass中持有的引用是指向同一个IDiet

稍后做

diet = new HerbivoreDiet();

您只需更改 Human class 之外的引用,它仍然是自己的副本,因此没有机会更改其引用。

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

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();
}