让我们说我是一个管理一堆代表的班级。出于某种原因,在某些时候我需要使堆栈上的随机委托无效,所以,因为我传递一个委托的变量作为引用,我希望设置为null该变量,将阻止堆栈调用该方法只测试是否它是null,但相反,对方法的引用仍然存在,因此将被调用。这里有一些愚蠢的代码来表明我的意思:
public delegate void DlgtVoidVoid();
public class TestPile {
Pile pileStack = new Pile();
DlgtVoidVoid _delvar1 = null;
DlgtVoidVoid _delvar2 = null;
public void Main()
{
_delvar1 = voidmethod1;
_delvar2 = voidmethod2;
pileStack.PushItem(ref _delvar1);
pileStack.PushItem(ref _delvar2);
//trying to invalidate the first delegate handler
_delvar1 = null;
pileStack.InvokeAndPop();//invoke 2
pileStack.InvokeAndPop();//shouldn't voidmethod1 call fail?
//but instead also voidmethod1 will be called: why?
}
void voidmethod1()
{
Console.WriteLine("calling void method one");
}
void voidmethod2()
{
Console.WriteLine("calling void method two");
}
}
这只是一个小堆栈管理器,只是一个例子:
public class Pile {
DlgtVoidVoid[] thepile = new DlgtVoidVoid[10];
int pileindex = -1;
public void PushItem(ref DlgtVoidVoid item)
{
thepile[++pileindex] = item;
}
public DlgtVoidVoid PeekItem()
{
return thepile[pileindex];
}
public DlgtVoidVoid PopItem()
{
return thepile[pileindex--];
}
public void InvokeAndPop()
{
if(pileindex >= 0) {
if(PeekItem() != null)
PopItem().Invoke();
}
}
}
很明显,在c#中传递一个参考变量并不像我预期的那样工作,比方说,在C ++或其中一些,因此有没有办法在C#中实现它必须改变代码逻辑吗?
答案 0 :(得分:1)
我发现你的代码设计得不是很好......将局部变量设置为NULL不会让任何人期望其他类内部数据发生任何变化。即使你能做到这一点,它肯定会迟早会导致错误。
最好在List<DlgtVoidVoid>
类中使用Pile
代替数组。而不是使委托引用为空,您最好创建一个特殊方法“Remove
”,随后将调用List
的相应方法。
public class Pile {
private List<DlgtVoidVoid> thepile = new List<DlgtVoidVoid>();
public void PushItem(DlgtVoidVoid item)
{
thepile.Add(item);
}
public DlgtVoidVoid PeekItem()
{
return thepile[thepile.Count-1];
}
public DlgtVoidVoid PopItem()
{
var item = thepile[thepile.Count-1];
thepile.RemoveAt(thepile.Count-1);
return item;
}
public void InvokeAndPop()
{
PopItem()();
}
public void Remove(DlgtVoidVoid deletageToRemove) {
thepile.Remove(deletageToRemove);
}
}
//...
public void Main()
{
_delvar1 = voidmethod1;
_delvar2 = voidmethod2;
pileStack.PushItem(ref _delvar1);
pileStack.PushItem(ref _delvar2);
//trying to invalidate the first delegate handler
pileStack.Remove(_delvar1);
pileStack.InvokeAndPop();//invoke 2
pileStack.InvokeAndPop();//Now it will throw you IndexOutOfRange exception. You could handle it better inside of the Pile methods
}
答案 1 :(得分:0)
通过引用传递与C ++几乎相同,这意味着您可以修改调用者中的变量值。但就像在C ++中一样,除非你存储了一个指向指针的指针,否则你仍然无法在以后更改该值。
在您的代码中,您存储的是对象,而不是对变量的引用,因此您必须调用remove方法来实际从数组中删除值。编辑变量不会对该对象的任何其他引用执行任何操作。
您当然可以拥有一个包含此委托的对象。然后,如果您修改该外部对象,则堆栈中的内部委托也将变为null,但它会添加另一个间接层,并且比调用remove方法更加混乱。