C# - 将EventArgs转换为通用事件类型

时间:2016-08-16 15:42:51

标签: c# generics events casting

我正在玩游戏只是为了好玩,我试图实现事件,但我收到了错误:

  

错误CS0305:使用泛型类型' Game.NPC.Events.MurderEventArgs'需要2个类型参数(CS0305)(游戏)

提升事件的代码:

public event EventHandler<EventArgs> OnMurderEvent;

public void RaiseMurderEvent<TVictim, TMurderer>(TVictim npcVictim, TMurderer npcMurderer)
{
    if (OnMurderEvent != null)
    {
        OnMurderEvent(this, new MurderEventArgs<TVictim, TMurderer>(npcVictim, npcMurderer));
    }
}

我处理事件的代码:

npc.OnMurderEvent += HandleMurderEvent;
npc.RaiseMurderEvent<Victim, Murderer>(null, null);

static void HandleMurderEvent(object sender, EventArgs e)
{
    Console.WriteLine((MurderEventArgs)e);
}

由于它没有通用类型,因此无法工作?

3 个答案:

答案 0 :(得分:1)

我不完全确定没有看到更多的代码,但因为它看起来像类型params TVictim,第二个代码片段的TMurderer对应于具体类型Victim,Murderer那么也许你的第二个片段只需要改变以下内容:

npc.OnMurderEvent += HandleMurderEvent;
npc.RaiseMurderEvent<Victim, Murderer>(null, null);

static void HandleMurderEvent(object sender, EventArgs e)
{
    Console.WriteLine((MurderEventArgs<Victim, Murderer>)e);
}

在回复您的评论时,要删除泛型(这似乎是大多数人在此推荐的内容),您可以将MurderEventArgs更改为:

public class MurderEventArgs : EventArgs
{
    public MurderEventArgs(IPerson victim, IPerson murderer)
    {
        Victim = victim;
        Murderer = murderer;
    }
    public IPerson Victim { get; }
    public IPerson Murderer { get; }
}

在举起事件的地方,它会是:

public event EventHandler<MurderEventArgs> OnMurderEvent;

public void RaiseMurderEvent(IPerson npcVictim, IPerson npcMurderer)
{
    if (OnMurderEvent != null)
        OnMurderEvent(this, new MurderEventArgs(npcVictim, npcMurderer));
}

在IPerson界面中会有一些常见信息,然后在专门的实现中会有更多信息 -

public interface IPerson
{
    string Name { get; }
}

public abstract class Person : IPerson
{
    protected Person (string name)
    {
        Name = name;
    }
    public string Name { get; }
}

public class Adult : Person
{
    public Adult(string name) : base(name) { }
}

public class Child : Person
{
    public Child(string name, int age) : base(name)
    {
        Age = age
    }
    public int Age { get; }
}

最后,事件监听代码将是这样的:

npc.OnMurderEvent += Npc_OnMurderEvent;
npc.RaiseMurderEvent(victim, murderer);

private static void Npc_OnMurderEvent(object sender, MurderEventArgs e)
{
    Console.WriteLine(e.Victim.Name + " was murdered by " + e.Murderer.Name);
    var murdererAsChild = e.Murderer as Child;
    if (murdererAsChild != null)
        Console.WriteLine(".. who is only a child, it must be Damien!");

    // Deal with any other special cases for particular Victim, Murderer combinations..
}

正如你在评论@ romain-aga(我无耻地从中偷走了IPerson接口名称)的回答时所说的那样,如果你对凶手或者更多的受害者感兴趣,就必须执行运行时转换。特定类型。

答案 1 :(得分:0)

使用接口,您可以获得解决方法。

如果您的课程:博士,学生,教师等...从同一个界面/班级实施或继承,您可以写下:

public event EventHandler<MurderEventArgs> OnMurderEvent;

public void RaiseMurderEvent(IPerson npcVictim, IPerson npcMurderer)
{
    if (OnMurderEvent != null)
    {
        OnMurderEvent(this, new MurderEventArgs(npcVictim, npcMurderer));
    }
}

并且这样做:

npc.OnMurderEvent += HandleMurderEvent;
npc.RaiseMurderEvent(null, null);

static void HandleMurderEvent(object sender, MurderEventArgs e)
{
    Console.WriteLine(e);
}

对于您的类层次结构,您可以使用类似的东西:

public interface IPerson
{
    // All the methods/properties that make a person
}

public abstract class Person : IPerson
{
    // All the common implementations
}

public class Doctor/Student/Teacher/etc... : Person
{
    // All the specific implementations
}

答案 2 :(得分:0)

假设您的MurderEventArgs看起来像这样:

class MurderEventArgs<TVictim, TMurderer> : EventArgs
{
    public MurderEventArgs(TVictim victim, TMurderer murderer)
    {
        //your code here
    }
}

您可以将EventHandler更改为:

class EventRaiser<TVictim, TMurderer>
{
    public event EventHandler<MurderEventArgs<TVictim, TMurderer>> OnMurderEvent;

    public void RaiseMurderEvent(TVictim npcVictim, TMurderer npcMurderer)
    {
        if (OnMurderEvent != null)
        {
            OnMurderEvent(this, new MurderEventArgs<TVictim, TMurderer>(npcVictim, npcMurderer));
        }
    }
}

您的使用方式如下:

static void FireAndForget<TVictim, TMurderer>(TVictim victim, TMurderer murderer)
{
    var npc = new EventRaiser<TVictim, TMurderer>();
    npc.OnMurderEvent += HandleMurderEvent<TVictim,TMurderer>;
    npc.OnMurderEvent += HandleMurderEventAlt;
    npc.RaiseMurderEvent(victim, murderer);
}

static void HandleMurderEvent<TVictim, TMurderer>(object sender, EventArgs e)
{
    var murderEvent = (MurderEventArgs<TVictim, TMurderer>)e;
    //do something with the event
}

//or alternatively
static void HandleMurderEventAlt<TVictim, TMurderer>(object sender, MurderEventArgs<TVictim, TMurderer> e)
{
    //do something with the event
}

一些事后

我将其命名为FireAndForget是有原因的。您需要为EventRaiserTVictim的每种类型组合创建一个新的TMurderer实例,或者重复使用已存在的实例进行组合。但在我看来,这违背了EventListener的目的。

您也可以创建一个public void Murder<TVictim, TMurderer>(TVictim victim, TMurderer murderer)方法并在那里完成您的工作。根本不需要活动。缺点:需要强制TMurderer调用该方法。

或者在任何MurdererBy<TMurderer>(TMurderer murderer)课程中实施TVictim并举起 MurderedByEvent 。缺点:TVictim &#34;知道&#34; TMurderer。让TMurderer实施一个Murders<TVictim>(TVictim victim)方法,但是谁强制执行谋杀?

或者创建某种 LifeCycleManager TVictim只能由该管理器创建(工厂模式)。在每个实例创建时,Manager将自己的EventListener注册到新TVictim实例的 MurderedByEvent

摘要

我上面的示例代码在某种程度上回答了您的问题,但您应该重新思考您的模型。谁需要知道这个事件?每个谋杀案都应该知道吗?受害者会怎么样?

谋杀事件需要知道什么?受害者和凶手的姓名只能使用其他作者提出的界面模型。

相关问题