接口不通过引用

问题描述:

我需要在运行时更改接口的实现。这个接口被许多类引用。接口不通过引用

这是我的测试案例,这是不工作,因为我期待。 (改变接口的参考似乎并不在其使用的所有场所更新)

这里是例子:

// 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)这样的方法将不是一个解决方案。

+1

你有什么期望和你观察到什么样的行为? “不更新”是什么意思?该参考是只读的;一旦你设置它,引用应该是不可变的。 – duffymo

+0

由于人类有一份参考文献('_diet'),并且您正在修改参考文献('饮食') – xanatos

+0

@Rahul的另一个副本,所以这基本上是构造函数注入。我不认为IoC或DI会改变任何东西。 – duffymo

既然你已经实现了一个门面,为什么不落实的第二个:

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

当你通过这个代码通过你的对象引用:

Human human = new Human(diet); 

参考(对象的地址)复制到它的参数:

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

他们3种不同的内存块包含与对象相同的引用:您的原始diet,参数变量(diet)和您的类属性(_diet)。

因此,当你执行你的代码:

diet = new HerbivoreDiet(); 

该内存块现在包含参照新的对象(HerbivoreDiet),但Human里面的一个仍然引用旧的对象。

+0

而参数被复制到一个字段:-)三个不同的内存块,其中一个的持续时间非常有限(参数) – xanatos

+0

@xanatos:是的,我错过了。这是一个错误,但这是造成这个问题的主要原因。 –

+0

明白了,它现在非常明显,你在想它... – Catalin

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

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

如果你说diet = new Diet()你用另一个对象的新指针覆盖本地变量diet。因为指针是按值传递的,所以Human引用不变。他们指出的对象是相同的(直到你覆盖饮食),但指针是彼此的副本。

你应该让饮食成为一个易于使用的,不可读的财产并改变它。或者创造一个新的人类。

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

这与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实例没有更新?

很简单,因为你的接口引用通过值通过就意味着你的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(); 
}