C# 接口不是通过引用传递的
我需要在运行时更改接口的实现。这个接口被许多类引用 这是我的测试用例,它没有像我期望的那样工作。(更改接口的引用似乎不会在其使用的所有位置更新) 以下是一个例子:C# 接口不是通过引用传递的,c#,asp.net,pointers,reference,C#,Asp.net,Pointers,Reference,我需要在运行时更改接口的实现。这个接口被许多类引用 这是我的测试用例,它没有像我期望的那样工作。(更改接口的引用似乎不会在其使用的所有位置更新) 以下是一个例子: // interface to change at runtime interface IDiet { void Eat(); } class CarnivoreDiet : IDiet { public void Eat() { Debug.WriteLine("Eat chicken");
// 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
)和类属性(\u diet
)
因此,当您执行代码时:
diet = new HerbivoreDiet();
此内存块现在包含对新对象的引用(
herbivoediet
),但Human
中的内存块仍然引用旧对象。diet
是一个局部变量,它的值是指向内存中实际对象的指针(地址)
如果您转到diet.SomeProperty=“foo”,则会更改内存中的对象,该对象会反映在所有位置
如果您说diet=new diet()
则使用指向另一个对象的新指针覆盖局部变量diet
Human
引用保持不变,因为指针是按值传递的。他们指向的对象是相同的(直到你覆盖饮食),但指针是彼此的副本
您应该使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"
}
这与接口无关。 代码所做的是通过值将变量传递给类构造函数,尽管该值是对实例的引用。然后将该值复制到新创建对象实例的成员。然后,当您更改最初用于保存该值的变量中的值时,它对类实例中的内部成员没有影响 这与
int
s完全相同。考虑这个样本:
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();
}
公共接口IProvider
{
公共T当前{get;set;}
}
公共类提供程序:IProvider
{
公共IDiet当前{get;set;}
}
班级:IDiet
{
私有只读IProvider\u提供程序;
公共人员(IProvider提供程序)
{
_dietProvider=dietProvider;
}
公共图书馆
{
_dietProvider.Current.Eat();
}
}
void Main()
{
IProvider dietProvider=新的dietProvider{Current=新的CarnivoreDiet()};
人类=新人类(饮食提供者);
人类。吃();
//“吃鸡肉”
dietProvider.Current=new HerbivoreDiet();
人类。吃();
}
你期望什么,你观察到什么行为?“不更新”是什么意思?参考是只读的;一旦你设置了它,引用应该是不可变的。因为人类有一个引用的副本(\u diet
),而你正在修改引用的另一个副本(diet
)@Rahul这本质上是构造函数注入。我不认为IoC或DI会改变任何东西。接口在这里是无关紧要的——我认为你基本上需要重新审视C#的工作方式。特别是,您似乎希望human.\u diet
字段链接到diet
局部变量。不是-将diet
的值复制到diet
参数中,然后将该参数复制到\u diet
字段中。以后对diet
局部变量的更改是不相关的。您可能需要读取并将参数复制到字段:-)三个不同的内存块,其中一个具有非常有限的持续时间(参数)@xanatos:是的,我错过了。这是一个错误,但这是这个问题的主要原因。