【问题标题】:Declare callback event handlers correctly正确声明回调事件处理程序
【发布时间】:2015-06-01 09:55:38
【问题描述】:

我有一个简单的委托、事件和属性,允许我为事件创建回调订阅:

public static class Test
{
    /// <summary>Delegate for property changed event</summary>
    public delegate void TestEventHandler();

    /// <summary>Event called when value is changed</summary>
    public static event TestEventHandler OnTestHappening;

    /// <summary>Property to specify our test is happening</summary>
    private static bool testHappening;
    public static bool TestHappening
    {
        get
        {
            return testHappening;
        }
        set
        {
            testHappening = value;

            // Notify our value has changed only if True
            // ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
            if ( value )
            {
                if ( OnTestHappening != null )
                    OnTestHappening();
            }
        }
    }
}

然后我可以轻松订阅和取消订阅该事件并根据需要触发事件回调:

public class Tester
{
    private void Main()
    {
        Testing();

        // Start the test
        Test.TestHappening = true;
    }

    private void Testing()
    {
        // Unsubscribe from event
        Test.OnTestHappening -= Testing;

        // Check if we're busy testing yet
        if ( !Test.TestHappening )
        {
            // Subscribe to event
            Test.OnTestHappening += new Test.TestEventHandler( Testing );

            return;
        }

        // Do stuff here....
    }
}

编译时,代码分析显示“CA1009:正确声明事件处理程序?”我搜索了高低,发现了很多问题、文章等,但没有一个感觉像他们解决我的场景。我似乎找不到转换的具体起点,我开始怀疑我是否打算完全重写实现?

编辑:首先,我非常感谢您的帮助,在发布此内容之前,我确实仔细浏览了所有网站,并且确实看到(并尝试使用)你们发布的每个链接。我什至又回去研究了代表和事件,但我觉得我不知何故错过了起点,因为每次我尝试更改其中的一部分时,我都会不断产生无法恢复的错误,例如:

public delegate void TestEventHandler( object sender, EventArgs e );

对于我访问的其他链接,我只能找到与我的代码(在委托、处理程序或属性中)的 1 个相似之处,但找不到与我的代码相关的任何内容以实际灌输那个“尤里卡”时刻

编辑 2:我现在已经用“看起来”作为正确标准重新构建了我的示例,但是这段代码非常丑陋,看起来就像是用 confogulus 棒殴打并浸入一罐 confutious 中然后被油炸太可怕了:

public static class Test
{
    /// <summary>Delegate for property changed event</summary>
    public delegate void TestEventHandler( object sender, EventArgs e );

    /// <summary>Event called when value is changed</summary>
    public static event TestEventHandler OnTestHappening;

    /// <summary>Property to specify our test is happening</summary>
    private static bool testHappening;
    public static bool TestHappening
    {
        get
        {
            return testHappening;
        }
        set
        {
            testHappening = value;

            // Notify our value has changed only if True
            // ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
            if ( value )
            {
                if ( OnTestHappening != null )
                    OnTestHappening( null, EventArgs.Empty );
            }
        }
    }
}

public class Tester
{
    private void Main()
    {
        Testing( this, EventArgs.Empty );

        // Start the test
        Test.TestHappening = true;
    }

    private void Testing( object sender, EventArgs e )
    {
        // Unsubscribe from the event
        Test.OnTestHappening -= Testing;

        // Check if we're busy testing yet
        if ( !GlobalClass.SystemOnline )
        {
            // Subscribe to the event
            Test.OnTestHappening += new Test.TestEventHandler( Testing );

            return;
        }

        // Do stuff here....
    }
}

请告诉我我遗漏了什么,实际上有一个更优雅的实现

编辑 3:基于 Enigmativity 的代码,我将代码重新编写为最基本的形式。我还以不同的方法移动了将变量设置为 true 的代码,因此它在 Main 中看起来并不那么愚蠢。

public static class Test4
{
    /// <summary>Event called when value is changed</summary>
    public static event EventHandler TestHappening;

    /// <summary>Property to specify our test is happening</summary>
    private static bool test = false;
    public static bool Test
    {
        get
        {
            return test;
        }
        set
        {
            // Notify our value has changed only if True
            // ie. Only fire an event when we're ready as we'll hook methods to the Event that must only fire if ready
            if ( value )
            {
                TestHappening( null, EventArgs.Empty );
            }
        }
    }
}

public class Tester4
{
    private void Main()
    {
        Testing( this, EventArgs.Empty );
    }

    private void Testing( object sender, EventArgs e )
    {
        // Unsubscribe from the event
        Test4.TestHappening -= Testing;

        // Check if we're busy testing yet
        if ( !Test4.Test )
        {
            // Subscribe to the event
            Test4.TestHappening += Testing;

            return;
        }

        // Do stuff here....
    }

    private void SomeMethodCalledFromSomewhere()
    {
        // Set the value to true and thereby start the test
        Test4.Test = true;
    }
}
  1. 这会被认为是好的代码,还是应该使用 Enigmativity 代码中定义的 OnTestHappening 方法?
  2. 为什么我不能使用无参数委托?它现在使用默认的( object sender, EventArgs e ),但这感觉有点矫枉过正,而且编译器对此感到满意并没有道理,但根据编码标准,它被认为是糟糕的代码?我不是在争论标准,而是试图理解它的推理。

【问题讨论】:

  • 你读过msdn.microsoft.com/en-us/library/ms182133.aspx 吗?特别是,看看你的代表的签名......
  • 您正在注册一个指向当前执行方法的事件处理程序。那是对的吗?你不是说把方法Go注册为eventhandler吗?
  • 嗯,这不是线程安全的,首先...您为什么不只订阅一次呢?基本上你有一个非常奇怪的情况,你在问题中根本没有提到......这与你遇到的问题并不真正相关,那就是你违反了事件约定然后表现得很惊讶一个约定检查代码分析工具在向你抱怨。
  • 如果你觉得你没有理由传递sender和一个空的EventArgs对象,那么不要,没关系。显然,一个旨在警告您违反 MS 编码标准的工具会在您不遵循这些标准时向您发出警告。只要您知道您没有遵循标准并且觉得您的代码在不遵循标准时更好,那么这就是您的选择。就其本质而言,风格选择是主观的。
  • @Storm:答案是“第一个是事件约定,第二个不是”。见msdn.microsoft.com/en-us/library/vstudio/ms229011。这只是一个约定 - 如果您违反它,您的代码仍然可以工作......

标签: c# events callback delegates


【解决方案1】:

根据 Storm 的要求,这是我最有可能构建代码的方式。它更符合标准约定。

public static class TestClass
{
    public delegate void TestEventHandler(object sender, EventArgs e);

    public static event TestEventHandler TestHappening;

    private static bool test = false;
    public static bool Test
    {
        get
        {
            return test;
        }
        set
        {
            test = value;
            if (test)
            {
                OnTestHappening();
            }
        }
    }

    private static void OnTestHappening()
    {
        var handler = TestHappening;
        if (handler != null)
            handler(null, EventArgs.Empty);
    }
}

Tester 看起来像这样:

public class Tester
{
    public void Main()
    {
        TestClass.TestHappening += Testing;
        Go();
    }

    private void Testing(object sender, EventArgs e)
    {
        Console.WriteLine(TestClass.Test);
        TestClass.TestHappening -= Testing;
    }

    private void Go()
    {
        TestClass.Test = true;
    }
}

调用它看起来像这样:

var tester = new Tester();
tester.Main();

运行此命令会将True 输出到控制台。


如果我以更标准的方式编写它,它会是这样的:

public class TestEventArg : EventArgs
{
    public TestEventArg(bool updatedValue)
    {
        this.UpdatedValue = updatedValue;
    }
    public bool UpdatedValue { get; private set; }
}

public class TestClass
{
    public event EventHandler<TestEventArg> TestHappening;

    private bool test = false;
    public bool Test
    {
        get { return test; }
        set
        {
            var old = test;
            test = value;
            if (test != old)
                OnTestHappening(test);
        }
    }

    private void OnTestHappening(bool updatedValue)
    {
        var handler = TestHappening;
        if (handler != null)
            handler(this, new TestEventArg(updatedValue));
    }
}

【讨论】:

  • 好的,根据您的代码,我在正确的轨道上,但现在在现有委托、事件和属性之上还有一个附加方法?这感觉有点矫枉过正,但我​​绝对可以看到代码维护和一致性方面的逻辑。我看到您也将 null 作为 sender 传递,因为它是一个静态类,一开始我很担心那个,传递 null 是否正确?我还更新了我的两个示例,希望更加清晰
  • @Storm - 这样做的主要目的是展示名称 OnTestHappening 的标准用法。它通常是类中的受保护方法,允许后代类引发事件(只有定义事件的类才能引发事件 - 甚至后代都不能)。在static 类中使用标准事件模式是您可能选择避免的——事实上,我认为它在非静态类中不是非常有用。在您的代码中,我会将其重新定义为 public delegate void TestEventHandler(); 并避免担心要传递的内容。
  • @Storm - 我的代码也是基于 your 代码。我认为您不能因为我的代码而得出结论说您走在正确的轨道上。我试图通过对您的代码进行最小 更改来调整您的代码,使其更符合标准约定。不要得出错误的结论。
  • @Storm - 我添加了一个更标准的解决方案。在这种情况下,由消费者决定是否忽略 False 值 - 应该如此。
  • 非常感谢您的代码,当您有工作基础时,它确实有助于解决某些问题。我现在最大的问题是,为什么我们需要使用带有senderEventArgs public delegate void TestEventHandler(object sender, EventArgs e) 的委托,而原来的工作很好public delegate void TestEventHandler(),因为我们对sender 或@ 不感兴趣987654337@?
猜你喜欢
  • 2023-03-26
  • 2021-11-05
  • 1970-01-01
  • 2016-04-26
  • 2012-04-20
  • 1970-01-01
  • 2010-09-22
  • 2016-05-26
  • 1970-01-01
相关资源
最近更新 更多