C#-事件订阅和变量覆盖

C#-事件订阅和变量覆盖,c#,.net,events,C#,.net,Events,我一直在摆弄静态事件,对一些事情很好奇 这是我为这些问题使用和修改的基本代码 类程序 { 静态void Main() { aa.col=null; col=新的aa.集合(新[]{“a”,“a”}); aa.evGatherstringa+=col.gatherstring; Console.WriteLine(aa.gatherstring()); //用于问题1 aa.evGatherstringa-=col.gatherstring; col=新的aa.集合(新[]{“b”,“b”});

我一直在摆弄静态事件,对一些事情很好奇

这是我为这些问题使用和修改的基本代码

类程序
{
静态void Main()
{
aa.col=null;
col=新的aa.集合(新[]{“a”,“a”});
aa.evGatherstringa+=col.gatherstring;
Console.WriteLine(aa.gatherstring());
//用于问题1
aa.evGatherstringa-=col.gatherstring;
col=新的aa.集合(新[]{“b”,“b”});
//用于问题2
aa.evGatherstringa+=col.gatherstring;
Console.WriteLine(aa.gatherstring());
}
公共静态aa级
{
公共委托字符串gatherstringa();
公共静态事件gatherstringa evGatherstringa;
公共静态字符串gatherstring(){return evGatherstringa.Invoke();}
公共类集合
{
公共集合(字符串[]字符串){this.strings=strings;}
公共字符串gatherstring()
{
返回此.strings[0];
}
公共字符串[]字符串{get;set;}
}
}
}
输出:

a
b
a
b
a
a
  • 更改代码和删除取消订阅时,Console.WriteLine输出仍然相同。为什么会发生这种情况?为什么这么糟糕
  • static void Main()
    {
    aa.col=null;
    col=新的aa.集合(新[]{“a”,“a”});
    aa.evGatherstringa+=col.gatherstring;
    Console.WriteLine(aa.gatherstring());
    //用于问题1
    //aa.evGatherstringa-=col.gatherstring;
    col=新的aa.集合(新[]{“b”,“b”});
    //用于问题2
    aa.evGatherstringa+=col.gatherstring;
    Console.WriteLine(aa.gatherstring());
    }
    
    输出:

    a
    b
    
    a
    b
    
    a
    a
    
  • 更改代码并删除取消订阅和重新订阅时,Console.WriteLine输出不同。为什么输出不是
    a
    然后是
    b
  • static void Main()
    {
    aa.col=null;
    col=新的aa.集合(新[]{“a”,“a”});
    aa.evGatherstringa+=col.gatherstring;
    Console.WriteLine(aa.gatherstring());
    //用于问题1和2
    //aa.evGatherstringa-=col.gatherstring;
    col=新的aa.集合(新[]{“b”,“b”});
    //用于问题2
    //aa.evGatherstringa+=col.gatherstring;
    Console.WriteLine(aa.gatherstring());
    }
    
    输出:

    a
    b
    
    a
    b
    
    a
    a
    
  • 更改代码和删除取消订阅时,Console.WriteLine输出仍然相同。为什么会发生这种情况?为什么这么糟糕
  • C#委托实际上是一个“多播”委托。也就是说,单个委托实例可以有多个调用目标。但当委托具有返回值时,只能使用一个值。在您的示例中,由于委托订阅的排序方式,如果删除第一个unsubscribe操作,则是订阅了事件的第二个委托,其返回值由事件的调用返回

    因此,在该特定示例中,取消订阅事件的第一个委托对返回的
    字符串
    值没有影响。即使两个委托都被调用,您仍然会从第二个委托实例返回
    字符串

    至于“为什么这么糟糕?”,嗯……是吗?这取决于上下文。我想说,这是一个很好的例子,说明了为什么应该避免使用委托类型而不是
    void
    返回类型的事件。至少可以这么说,如果有多个返回值,但只看到调用实际返回的其中一个值,这可能会令人困惑

    至少,如果您确实为事件使用这样的委托类型,那么您应该愿意接受默认行为,或者将多播委托实例分解为其各自的调用目标(请参阅),并明确决定自己想要哪个返回值

    如果您确实知道自己在做什么,并且熟悉多播委托的工作方式,并且对丢失除一个之外的所有返回值(或者在引发事件的代码中显式捕获所有返回值)的想法感到满意,那么我不会说这本身就一定是“坏”的。但它肯定是非标准的,如果不小心执行,几乎肯定意味着代码不能按预期工作。这是不好的。:)

  • 更改代码并删除取消订阅和重新订阅时,Console.WriteLine输出不同。为什么输出不是a然后是b
  • 由于您修改了
    col
    变量,因此您希望以前订阅的事件处理程序会自动引用分配给
    col
    变量的新实例。但这不是事件订阅的工作方式

    首次订阅事件时,使用
    aa.evGatherstringa+=col.gatherstring
    ,则
    col
    变量仅用于提供对找到事件处理程序方法的
    aa.collection
    实例的引用。事件订阅仅使用该实例引用。事件订阅不观察变量本身,因此以后对变量的更改也不会影响事件订阅

    相反,
    aa.collection
    对象的原始实例仍然订阅该事件。即使修改了
    col
    变量,再次引发事件仍会调用原始对象中的事件处理程序,而不是现在分配给
    col
    变量的新对象

    更一般地说,您需要非常小心,不要将实际对象与其可以存储在不同位置的引用以及存储在不同位置的任何单个变量混淆