繁体   English   中英

c#从另一个类中的另一个静态事件引发事件

[英]c# raise event from another static event in another class

需要帮助另一个班级的活动。 我有宣告事件的课程:

     public class MxPBaseGridView : GridView
    {

        public event AddNewItemsToPopUpMenuEventHandler AddNewItemsToPopUpMenu;
          ...
    }

我需要从中调用事件的另一个类具有方法和“ AddNewItemsToPopUpMenuEventHandler”委托

    public delegate void AddNewItemsToPopUpMenuEventHandler(PopupMenuShowingEventArgs e);
    public static class GridViewUtils
{
public static void gridView_PopupMenuShowing(object sender, PopupMenuShowingEventArgs e)
    {                     
        if (e.MenuType != DevExpress.XtraGrid.Views.Grid.GridMenuType.Row)
        {
           if (menu != null)
            {               
                if (sender is MxPBaseAdvBandedGridView)
                {
                    MxPBaseAdvBandedGridView currentGrid = sender as MxPBaseAdvBandedGridView;

...

                    currentGrid.AddNewItemsToPopUpMenu();
                    if (currentGrid.AddNewItemsToPopUpMenu != null) //there i need to call event
                        currentGrid.AddNewItemsToPopUpMenu(e); // how you understand it doesn't work 
                }

那么做同样工作的正确方法是什么?

C#中的事件是一种有趣的事情。 它们非常类似于自动属性,但是具有私有的get方法和公共的(或您选择的任何访问方式)set方法。

请允许我示范。 让我们用一个假设事件创建一个假设类。

class SomeObject{
    public event EventHandler SomeEvent;

    public void DoSomeStuff(){
        OnSomeEvent(EventArgs.Empty);
    )

    protected virtual void OnSomeEvent(EventArgs e){
        var handler = SomeEvent;
        if(handler != null)
            handler(this, e);
    }
}

此类遵循暴露事件的类的典型模式。 它公开地公开事件,但是具有受保护的虚拟“ On ...”方法,默认情况下,该方法仅简单地调用事件(如果它具有任何子用户)。 这种受保护的虚拟方法不仅封装了实际调用事件的逻辑,而且还为派生类提供了一种方法:

  • 方便地以较少的开销处理事件,
  • 在所有外部订户收到事件之前或之后执行一些处理,
  • 呼叫完全不同的事件,或者
  • 完全抑制该事件。

但是,这个名为“ SomeEvent”的“事件”对象是什么? 在C#中,我们熟悉字段,属性和方法,但是事件究竟是什么?

在开始讨论之前,它有助于认识到C#中实际上只有两种类型的类成员:字段和方法。 属性和事件或多或少只是这些之上的语法糖。

属性实际上是一个或两个方法,并且存储在元数据中的名称可供C#编译器用来引用这两个方法之一。 也就是说,当您定义这样的属性时:

public string SomeProperty{
    get{return "I like pie!";}
    set{
        if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
            Console.WriteLine("Pie is yummy!");
        else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
    }
}

编译器为您编写了两种方法:

public string get_SomeProperty(){return "I like pie!";}
public void   set_SomeProperty(string value){
    if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
        Console.WriteLine("Pie is yummy!");
    else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
}

我不是这个意思。 从字面上看,这两个方法与有关属性的大量元数据一起成为编译类的一部分,当从(获取)或写入(设置)属性时,这将告诉编译器下次调用哪个方法。 因此,当您编写这样的代码时:

var foo = someObject.SomeProperty;
someObject.SomeProperty = foo;

编译器找到分配给SomeProperty的getter和setter方法,并将您的代码转换为:

string foo = someObject.get_SomeProperty();
someObject.set_SomeProperty(foo);

这就是为什么如果您定义一个具有公共字段的类,但是后来决定将其更改为属性,以便在读取或写入该类时可以做一些有趣的事情,则必须重新编译包含对此引用的所有外部程序集成员,因为原来是字段访问指令,而必须变成方法调用指令。

现在,此属性有些异常,因为它不依赖任何后备字段。 它的getter返回一个恒定值,而它的setter没有将其值存储在任何地方。 需要明确的是,这是完全正确的,但是在大多数情况下,我们更像这样定义属性:

string someProperty;

public string SomeProperty{get{return someProperty;}set{someProperty = value;}}

该属性除了读取和写入字段外不执行其他任何操作。 它与名为SomeProperty的公共字段几乎相同,不同之处在于,您可以稍后在该getter和setter中添加逻辑,而无需重新编译类的使用者。 但是这种模式非常普遍,以至于C#3添加了“自动属性”以达到相同的效果:

public string SomeProperty{get;set;}

编译器将其转换为与上面编写的代码相同的代码,不同的是后备字段具有仅编译器才知道的超级秘密名称,因此即使在类本身内,我们也只能引用代码中的属性。

由于我们无法访问后备字段,因此您可能具有以下只读属性:

string someProperty;

public string SomeProperty{get{return someProperty;}}

您几乎永远不会看到只读的自动属性(编译器允许您编写它们,但几乎找不到它们的用法):

public string SomeProperty{get;} // legal, but not very useful unless you always want SomeProperty to be null

相反,您通常会看到的是:

public string SomeProperty{get;private set;}

附加到setprivate access修饰符使类内的方法可以设置属性,但是该属性在类外仍然显示为只读。

“现在,这与事件有什么关系?” 你可能会问。 嗯,事实上,事件非常类似于自动属性。 通常,当您声明事件时,编译器会生成一个超级秘密后备字段和一对方法。 除了背景字段不是超级秘密,并且这两种方法不是“获取”和“设置”之外,它们都是“添加”和“删除”。 让我示范一下。

当您编写这样的事件时:

public event EventHandler SomeEvent;

编译器写的是这样的:

EventHandler SomeEvent;

public void add_SomeEvent(EventHandler value){
    SomeEvent = (EventHandler)Delegate.Combine(SomeEvent, value);
}
public void remove_SomeEvent(EventHandler value){
    SomeEvent = (EventHandler)Delegate.Remove(SomeEvent, value);
}

它还添加了一些元数据胶水,以便稍后在您编写如下代码时:

void Awe_SomeEventHandler(object sender, EventArgs e){}

void SomeMethod(SomeObject Awe){
    Awe.SomeEvent += Awe_SomeEventHandler
    Awe.SomeEvent -= Awe_SomeEventHandler
}

编译器将其重写为(仅有趣的行):

Awe.add_SomeEvent(Awe_SomeEventHandler);
Awe.remove_SomeEvent(Awe_SomeEventHandler);

这里需要注意的重要一点是,与SomeEvent相关的唯一可公开访问的成员是那些add和remove方法,并且在使用+=-=运算符时会调用它们。 支持字段(该代理对象名为SomeEvent,用于保存事件的订阅者)是一个私有字段,只有声明类的成员才能访问。

但是,就像自动属性只是手动编写支持字段以及getter和setter的快捷方式一样,您可以显式声明委托以及添加和删除方法:

internal EventHandler someEvent;

public event EventHandler SomeEvent{
    add{someEvent = (EventHandler)Delegate.Combine(someEvent, value);}
    remove{someEvent = (EventHandler)Delegate.Remove(someEvent, value);}
}

然后,程序集中的其他类可以触发您的事件:

var handler = Awe.someEvent;
if(handler != null)
    handler(Awe, EventArgs.Empty);

但是,以常规(自动)方式定义事件,并只公开“提高”方法会更容易且更惯用:

internal void RaiseSomeEvent(){OnSomeEvent(EventArgs.Empty);}

但是现在您希望能理解为什么必须这样做,以及背景情况。

您只能在已定义事件的类中调用事件。 常见的是使用特定的方法来触发事件,您必须在定义事件的类中添加该方法。 对于您的情况,在类MxPBaseGridView中。 添加以下内容:

public void OnAddNewItemsToPopUpMenu(<eventargstype> e) {
    var addNewItemsToPopUpMenu = AddNewItemsToPopUpMenu;
    if (addNewItemsToPopUpMenu != null)
        addNewItemsToPopUpMenu(this, e);
}

注意:我不确定eventargs-type是什么,所以我将其保持打开状态。

然后,您可以从静态方法中调用此方法。

注意:通常,我将On ...方法定义为私有方法,必要时将其定义为受保护的方法。 在这种情况下,我将其定义为public,因为您需要从课堂之外调用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM