接口不通过引用
我需要在运行时更改接口的实现。这个接口被许多类引用。接口不通过引用
这是我的测试案例,这是不工作,因为我期待。 (改变接口的参考似乎并不在其使用的所有场所更新)
这里是例子:
// 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)
这样的方法将不是一个解决方案。
既然你已经实现了一个门面,为什么不落实的第二个:
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
里面的一个仍然引用旧的对象。
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();
}
你有什么期望和你观察到什么样的行为? “不更新”是什么意思?该参考是只读的;一旦你设置它,引用应该是不可变的。 – duffymo
由于人类有一份参考文献('_diet'),并且您正在修改参考文献('饮食') – xanatos
@Rahul的另一个副本,所以这基本上是构造函数注入。我不认为IoC或DI会改变任何东西。 – duffymo