gpt4 book ai didi

c# - 帮助我以正确的方式编写此事件

转载 作者:太空狗 更新时间:2023-10-30 01:11:09 25 4
gpt4 key购买 nike

原始问题

我至少阅读了十几篇关于事件的文章和 SO 线程,并弄清楚了基本的想法,但我对如何以正确的方式做到这一点有些困惑。似乎至少有两种常见的事件编写方法,一种比另一种更值得推荐。

我看到很多 Material 中作者跳过了部分过程,并假设出于某种原因您会知道这一点。还有很多教程,如“MyEventExample”和“SomeProcessGoesHere”,这使得理解整个示例变得更加困难。很多例子都在教你如何做某事,但最后却声明你在现实中当然永远不会那样做——但它们并没有提供你 去做。

最后,使用事件的场景的每个组件的命名约定似乎无处不在。我很难弄清楚某些概念在何处应用,因为每个人都以不同的方式命名它的每一部分。

所以这就是我所追求的:我在游戏中有一个简单的情况,它会利用事件。我希望有人参与并编写事件接线,演示推荐的方法以及最常见的命名和结构约定。我知道要求为我编写代码是一种糟糕的形式,但我确实在寻找编写代码的方式,以便我可以自信地开始自己编写代码。

请忽略这是否是一个好的游戏设计,甚至是否适合举办事件。我只是对如何正确编写事件内容感兴趣,这是我的示例空间。


//In my game I have a number of entities which can 'talk' to the player.
//An NPC can greet them, a zone might 'greet' them by displaying "Cityville"
//when they enter it, their inventory might tell them they're out of space,
//and so on. Everything would pass a Message object to a Messenger object,
//which would then deliver the messages as it saw fit.

public class Messenger
{
private Queue<Message> _messages = new Queue<Message>();;
private Stack<Message> _archive = new Stack<Message>();;

public IEnumerable<Message> Messages { get { return _messages.AsEnumerable(); } }
public IEnumerable<Message> Archive { get { return _archive.AsEnumerable(); } }

public void Add(Message message)
{
_messages.Enqueue(message);
}
public void Deliver()
{
Message msg = _messages.Dequeue();
_archive.Push(msg);

//Here's where I'd broadcast to any subsystem listening
//that the message was delivered
//Event args should be (_archive.Peek(), DateTime.Now);

}

public event MessageDeliveryEvent Delivery;
protected virtual void OnDelivery(MessageHandlerDeliveryEventArgs e)
{
if (this.Delivery != null) { this.Delivery(this, e); }
}
}

//Okay, here's my delegate declared outside any class. One tutorial suggested
//putting it in the same file as the event arguments class so it would be
//easier to find, which sounds reasonable to me, but I dunno.

public delegate void MessageDeliveryEvent(object sender, MessageHandlerDeliveryEventArgs e);

//I've seen examples where they don't even write an event arguments class.
//I think you could probably just pass the same stuff directly, but the
//separate class sounds like a good idea, more flexible if things change.

public class MessageHandlerDeliveryEventArgs : EventArgs
{
private readonly Message _message;
private readonly DateTime _delivered;

public MessageHandlerDeliveryEventArgs(Message message, DateTime delivered)
{
_message = message;
_delivered = delivered;
}

public Message Message { get { return _message; } }
public DateTime DeliveryDateTime { get { return _delivered; } }
}

//So in the UI layer I'd have things like a ChatBox which would be a
//scrolling list of everything said to the player. There would also be a
//GeneralInfoBox control that displayed things like what zone you just
//entered, or that your inventory is full. Both would listen to the
//Messenger object for a delivery event, and then check the Message object
//associated with that event to see if they needed to handle the display
//of that message.

public class ChatBox
{
//OMG there's a new message, lemme see if I should display it
private void TheThingThatListensToMessengerEvents(Message message, DateTime datetime)
{
if Message.Type == MessageType.Chat { Print(datetime.ToString() + ": " + message.Text); }
}
public string Print(string text) {}
}
public class GeneralInfoBox
{
//OMG there's a new message, lemme see if I should display it
private void TheThingThatListensToMessengerEvents(Message message)
{
if Message.Type == MessageType.General { Print(message.Text); }
}
public string Print(string text) {}
}

如果我能澄清任何事情,请告诉我。如果有一个非常好的教程我显然错过了,请随时指出我。提前致谢。


我从线程中获取的内容

这是我的示例,其中包含事件。也许它会帮助像我一样思考的其他人(上帝帮助他们)想象它。


public class MessageHandler
{
private Queue<Message> _messages = new Queue<Message>();
private Stack<Message> _archive = new Stack<Message>();

public MessageHandler() { }

public IEnumerable<Message> Messages { get { return _messages.AsEnumerable(); } }
public IEnumerable<Message> Archive { get { return _archive.AsEnumerable(); } }

public void Add(Message message)
{
_messages.Enqueue(message);
}
public void Deliver()
{
Message msg = _messages.Dequeue();
_archive.Push(msg);

//Call the method which broadcasts the event
OnDelivery(new MessageDeliveryEventArgs(_archive.Peek(), DateTime.Now));
}

//The event
public event EventHandler<MessageDeliveryEventArgs> Delivery;

//The method which broadcasts the event
protected virtual void OnDelivery(MessageDeliveryEventArgs messageDeliveryEventArgs)
{
EventHandler<MessageDeliveryEventArgs> handler = Delivery;
if (handler != null) { handler(this, messageDeliveryEventArgs); }
}
}

//The event arguments class for the event of interest. Carries information about this kind of event
public class MessageDeliveryEventArgs : EventArgs
{
private readonly Message _message;
private readonly DateTime _delivered;

public MessageDeliveryEventArgs(Message message, DateTime delivered)
{
_message = message;
_delivered = delivered;
}

public Message Message { get { return _message; } }
public DateTime DeliveryDateTime { get { return _delivered; } }
}

//A UI control which listens for an event in a Messenger object
public class ChatBox
{
//Specify the instance of the Messenger class to whose event(s) we plan to subscribe
public ChatBox(MessageHandler messenger)
{
//Subscribe this control's OnDelivery method to the Delivery event of the specified instance of Messenger
messenger.Delivery += this.OnDelivery;
}

//The method which we intend to subscribe to the Delivery event of an instance of Messenger
private void OnDelivery(object sender, MessageDeliveryEventArgs e)
{
if (e.Message.Format == MessageFormat.Local)
{
Print(String.Format("{0}: {1}", e.DeliveryDateTime, e.Message.Text));
}
}
private void Print(string text) { }
}

最佳答案

这是设置标准 .Net 事件所遵循的典型约定的示例:

using System;

namespace ObserverExample
{
class Program
{
static void Main(string[] args)
{
var subject = new Subject();
var observer = new Observer();
observer.Observe(subject);
subject.SomeAction();
Console.ReadLine();
}
}

public class Subject
{
public event EventHandler<TopicEventArgs> TopicHappening;

public void SomeAction()
{
OnTopicHappening(new TopicEventArgs("Hello, observers!"));
}

protected virtual void OnTopicHappening(TopicEventArgs topicEventArgs)
{
EventHandler<TopicEventArgs> handler = TopicHappening;

if (handler != null)
handler(this, topicEventArgs);
}
}

public class TopicEventArgs : EventArgs
{
public TopicEventArgs(string message)
{
Message = message;
}

public string Message { get; private set; }
}

public class Observer
{
public void Observe(Subject subject)
{
subject.TopicHappening += subject_TopicHappening;
}

void subject_TopicHappening(object sender, TopicEventArgs e)
{
Console.WriteLine(e.Message);
}
}
}


此示例中涉及的三个主要类是 Subject , Observer , 和 TopicEventArgs . Program类仅用于为示例提供驱动程序。

首先查看 Program.Main() 方法,首先实例化 Subject(将引发事件的对象)和 Observer(将订阅引发的事件的对象)的实例。接下来,向观察者传递一个主题实例,使其有机会订阅任何所需的事件。最后,调用主体的 SomeAction() 方法,这会引发事件。

查看主题,我们看到名为 TopicHappening 的事件 EventHandler<TopicEventArgs>被公开宣布。 EventHandler 类型是在 .Net 2.0 中引入的,它允许在不必显式定义委托(delegate)类型的情况下声明事件。 Subject 类也有两个方法,SomeAction()OnTopicHappening() .方法SomeAction()表示应用程序中的点,Subject 执行一些它想要通知世界(即“任何观察者”)的任务。方法OnTopicHappening(TopicEventArgs)方法提供类中将引发事件的逻辑点。首先,请注意它遵循 On[事件名称] 的命名约定。虽然此方法可以命名为任何名称,但此模式已按惯例广泛采用。其次,请注意它被定义为采用 TopicEventArgs 类型的单个参数。这也遵循标准约定,目的是在引发事件的逻辑点(在 SomeAction() 方法中)而不是从引发事件的物理点来决定引发哪些事件参数。第三,注意它被声明为 protected 虚拟。通常遵循此模式以允许任何扩展 Subject 的类覆盖引发 TopicHappening 事件时究竟发生了什么。在 OnTopicHappening() 方法中,TopicHappening 事件在检查是否为 null 并被调用之前被分配给一个单独的变量。这是为了避免可能的竞争条件,即事件可能在检查 null 之后但在调用事件之前被另一个线程(即所有观察者取消订阅)清除。

查看 TopicEventArgs 类,这代表我们的主题提出的事件主题。当主题需要将信息与引发的事件相关联时,通常会创建自定义 EventArgs 类。对于只希望发送没有任何关联参数的信号事件的主题,应使用基 EventArgs.Empty 类。

最后,Observer 类定义将从 Subject 接收事件通知的对象。在此示例中,Observer 类公开了一个 Observe() 方法,作为接收对 Subject 实例的引用的一种方式。在该方法中,一个名为 subject_TopicHappening 的私有(private)事件处理程序方法被分配给主题上的 TopicHappening 事件。此名称格式是 Visual Studio 在注册处理事件时键入 += 时自动生成的委托(delegate)的结果。这实质上是将此方法添加到方法集合中,以便在主题引发事件时调用。调用时,私有(private) subject_TopicHappening 方法只是将事件参数中包含的消息写入控制台。

希望这对您有所帮助。

关于c# - 帮助我以正确的方式编写此事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3272042/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com