理解泛型和Func参数

时间:2015-07-22 20:04:51

标签: c# generics unity3d

如果在某个地方得到回答,我不会感到惊讶,问题是我不知道如何用短语搜索来找到我需要的东西。我已经找到的东西要么过于简单,要么无法使用,要么解释得不好,以至于我无法将其翻译成我自己的项目。我没有关于事件处理程序,代表等的正式指导(哎呀,我甚至没有学习实体组件系统 - 或其他设计模式 - 直到我大学毕业后很久才被聘为程序员,即便如此,这也不是我在工作中学到的东西,也不是我的工作。

基本上我想知道的是,Array.Sort< T>(T []数组,比较< T>比较)的定义看起来是什么样的?

显然会出现某种泛化现象,因为myCompareDelegate(...)采用任何类型的两个参数。在我发现的与Func参数有关的几乎所有内容中,一个Func<>参数需要显式声明的类型,但使用我不熟悉的运算符的一些示例代码除外:

//Entity implements IEventDispatcher
public SomeConstructor(Entity ent) {
    //public delegate void EventListener(EventBase eventData); is declared
    //in the IEventDispatcher interface.
    ent.attachEvent(typeof(EntityEventPreRender),  new EventListener(onEvent));
    ent.attachEvent(typeof(EntityEventPostRender),  new EventListener(onEvent));
}
//EntityEventPreRender extends EntityEventRender extends EntityEvent extends EventBase
//EntityEventPostRender extends EntityEventRender extends EntityEvent extends EventBase
public void onEvent(EventBase data) {
    if(data is EntityEventPreRender)
        onPre((EntityEventPreRender)data);
    if(data is EntityEventPostRender)
        onPost((EntityEventPostRender)data);
}
public void onPre(EntityEventPreRender evt) {}
public void onPost(EntityEventPostRender evt) {}

编译但我不知道做什么因此,我不知道如何利用它来创建将运行或执行我想要的代码要做。

我的目标是能够创建一个事件系统(是的,我知道C#内置了一个事件系统,但同样,我看到的所有示例代码都被简化到了无用的地步 - 与调度程序包含在同一类中的listeners - 或者复杂且无法解释的)。我希望以下内容成立:

  1. 注册事件监听器的单个函数(适用于任何类型的事件及其子类型)
  2. 调度事件的单个函数(仅调用相关的侦听器)
  3. 能够创建新的事件类型,而无需修改注册和处理的功能(在基本事件类之外的调度程序中没有显式类型),前提是新事件类型扩展了允许的事件类型(即实体只会dispatch EntityEvents而不是WorldEvents)。
  4. 我有一个当前有效的系统,但它要求我的所有处理程序都通过一个“onEvent”函数,该函数接受一个基本事件对象,并确定它的实际类型是什么,将其传递给真正的处理程序。 / p>

    例如:

    public SomeConstructor(Entity ent) {
        ent.attachEvent(onPre);
        ent.attachEvent(onPost);
    }
    public void onPre(EntityEventPreRender evt) {}
    public void onPost(EntityEventPostRender evt) {}
    

    attachEvent()这里是一个函数,它接受一个Type(用作HashMap键)和一个Delegate并将它存储在一个列表中(HashMap值)。调度事件只需要传递EventData对象,查询其类型(通过evt.GetType())来检索侦听器列表,然后调用它们:listItem(evt)

    但我宁愿能够这样做:

    {{1}}

    但我不能,为了我的生活,弄清楚如何做到这一点,因为我不知道如何声明attachEvent()函数采用通用函数参数的方式Array.Sort< T>(T []数组,比较< T>比较)。我收到错误:

      

    “方法doSomething< T>(SomeClass.Thing< T>)的类型参数”无法从用法中推断出来。请尝试明确指定类型参数。“

1 个答案:

答案 0 :(得分:1)

我认为您可能正在寻找以下内容:

public  static  class   PubSub<TMessage>
{
    private static  List
                    <
                        Action
                        <
                            TMessage
                        >
                    >                   listeners   = new List<Action<TMessage>>();

    public  static  void                Listen(Action<TMessage> listener)
    {
        if (listener != null)   listeners.Add(listener);
    }

    public  static  void                Unlisten(Action<TMessage> listener)
    {
        if (listeners.Contains(listener))   listeners.Remove(listener);
    }

    public  static  void                Broadcast(TMessage message)
    {
        foreach(var listener in listeners)  listener(message);
    }

}

在上面的代码中,使用PubSub并为TMessage指定类型会在内存中创建一个新的静态类,并为其存储一个单独的侦听器列表分配自己的内存空间。只要您始终使用基类型作为TMessage类型参数的类型参数,编译器将确保只允许TMessage及其子类的替换类型。

然后你会这样使用它:

public  class   SomeMessageType
{
    public  int     SomeId;
    public  string  SomeDescription;
}

public  class   SomePublisher
{

    public  void    DoSomethingCool(string description)
    {
        var randomizer  = new Random();
        ...
        PubSub<SomeMessageType>.Broadcast(new SomeMessageType(){SomeId = randomizer.Next(), SomeDescription = description});
    }

}

public  class   SomeListener
{

    static                  SomeListener()
    {
        PubSub<SomeMessageType>.Listen(SomeMessageEvent);
    }

    private static  void    SomeMessageEvent(SomeMessageType message)
    {
        // do something with the message
    }

}

如果然后创建另一个SomeOtherMessageType类,它不从SomeMessageType继承并对它进行类似的调用,它只会广播到该特定类型的侦听器。

编辑:

以下是一个完整的概念证明,它可以编辑您可以在控制台应用程序中运行,以消除您可能对此技术的效果所产生的任何疑虑。

using System;
using System.Collections.Generic;

namespace TestPubSub
{


    public  class   Program
    {

        public  static  void    Main(string[] args)
        {
            Program.startListeners();

            Program.sendTestMessages();

            Program.stopConsoleFromExitingImmediately();
        }

        private static  void    startListeners()
        {
            SomeListener.Listen();
            SomeOtherListener1.Listen();
            SomeOtherListener2.Listen();
        }

        private static  void    sendTestMessages()
        {
            var publisher1  = new SomePublisher();
            var publisher2  = new SomeOtherPublisher();

            publisher1.DoSomethingCool("Hello world");
            publisher2.DoSomethingElse(DateTime.Now);
        }

        private static  void    stopConsoleFromExitingImmediately()
        {
            Console.ReadKey();
        }

    }

    public  static  class   PubSub<TMessage>
    {
        private static  List
                        <
                            Action
                            <
                                TMessage
                            >
                        >                   listeners   = new List<Action<TMessage>>();

        public  static  void                Listen(Action<TMessage> listener)
        {
            if (listener != null)   listeners.Add(listener);
        }

        public  static  void                Unlisten(Action<TMessage> listener)
        {
            if (listeners.Contains(listener))   listeners.Remove(listener);
        }

        public  static  void                Broadcast(TMessage message)
        {
            foreach(var listener in listeners)  listener(message);
        }

    }

    public  class   SomeMessageType
    {
        public  int     SomeId;
        public  string  SomeDescription;
    }

    public  class   SomeOtherMessageType
    {
        public  DateTime    SomeDate;
        public  Double      SomeAmount;
    }

    public  class   SomePublisher
    {

        public  void    DoSomethingCool(string description)
        {
            var randomizer  = new Random();
            PubSub<SomeMessageType>.Broadcast(new SomeMessageType(){SomeId = randomizer.Next(), SomeDescription = description});
        }

    }

    public  class   SomeOtherPublisher
    {

        public  void    DoSomethingElse(DateTime when)
        {
            var randomizer  = new Random();
            PubSub<SomeOtherMessageType>.Broadcast(new SomeOtherMessageType(){SomeAmount = randomizer.NextDouble(), SomeDate = when});
        }

    }

    public  class   SomeListener
    {

        public  static  void    Listen()
        {
            PubSub<SomeMessageType>.Listen(SomeMessageEvent);
        }

        private static  void    SomeMessageEvent(SomeMessageType message)
        {
            Console.WriteLine("Attention! SomeMessageType receieved by SomeListener with\r\nid: {0}\r\ndescription: {1}\r\n", message.SomeId, message.SomeDescription);
        }

    }

    public  class   SomeOtherListener1
    {

        public  static  void    Listen()
        {
            PubSub<SomeOtherMessageType>.Listen(SomeMessageEvent);
        }

        private static  void    SomeMessageEvent(SomeOtherMessageType message)
        {
            Console.WriteLine("Heads up! SomeOtherMessageType receieved by SomeOtherListener1 with\r\namount: {0}\r\ndate: {1}\r\n", message.SomeAmount, message.SomeDate);
        }

    }

    public  class   SomeOtherListener2
    {

        public  static  void    Listen()
        {
            PubSub<SomeOtherMessageType>.Listen(SomeMessageEvent);
        }

        private static  void    SomeMessageEvent(SomeOtherMessageType message)
        {
            Console.WriteLine("Yo! SomeOtherMessageType receieved by SomeOtherListener2 withr\namount: {0}\r\ndate: {1}\r\n", message.SomeAmount, message.SomeDate);
        }

    }

}

再次编辑(使用基于实例的pubs的替代概念证明):

以下是使用基于实例的PubSub的概念证明。

using System;
using System.Collections.Generic;

namespace TestPubSub
{


    public  class   Program
    {

        private static  PubSub<SomeMessageType>         pubSub1     = new PubSub<SomeMessageType>();
        private static  PubSub<SomeOtherMessageType>    pubSub2     = new PubSub<SomeOtherMessageType>();
        private static  SomeListener                    listener1   = new SomeListener();
        private static  SomeOtherListener1              listener2   = new SomeOtherListener1();
        private static  SomeOtherListener2              listener3   = new SomeOtherListener2();

        public  static  void                Main(string[] args)
        {
            Program.startListeners();

            Program.sendTestMessages();

            Program.stopConsoleFromExitingImmediately();
        }

        private static  void    startListeners()
        {
            Program.listener1.Listen(Program.pubSub1);
            Program.listener2.Listen(Program.pubSub2);
            Program.listener3.Listen(Program.pubSub2);
        }

        private static  void    sendTestMessages()
        {
            var publisher1  = new SomePublisher(Program.pubSub1);
            var publisher2  = new SomeOtherPublisher(Program.pubSub2);

            publisher1.DoSomethingCool("Hello world");
            publisher2.DoSomethingElse(DateTime.Now);
        }

        private static  void    stopConsoleFromExitingImmediately()
        {
            Console.ReadKey();
        }

    }

    public  class   PubSub<TMessage>
    {
        private List
                <
                    Action
                    <
                        TMessage
                    >
                >                   listeners   = new List<Action<TMessage>>();

        public  void                Listen(Action<TMessage> listener)
        {
            if (listener != null)   this.listeners.Add(listener);
        }

        public  void                Unlisten(Action<TMessage> listener)
        {
            if (listeners.Contains(listener))   this.listeners.Remove(listener);
        }

        public  void                Broadcast(TMessage message)
        {
            foreach(var listener in this.listeners) listener(message);
        }

    }

    public  class   SomeMessageType
    {
        public  int     SomeId;
        public  string  SomeDescription;
    }

    public  class   SomeOtherMessageType
    {
        public  DateTime    SomeDate;
        public  Double      SomeAmount;
    }

    public  class   SomePublisher
    {

        private PubSub<SomeMessageType> pubSub;

        public                          SomePublisher(PubSub<SomeMessageType> pubSub)   { this.pubSub = pubSub; }

        public  void                    DoSomethingCool(string description)
        {
            var randomizer  = new Random();
            this.pubSub.Broadcast(new SomeMessageType(){SomeId = randomizer.Next(), SomeDescription = description});
        }

    }

    public  class   SomeOtherPublisher
    {

        private PubSub<SomeOtherMessageType>    pubSub;

        public                                  SomeOtherPublisher(PubSub<SomeOtherMessageType> pubSub) { this.pubSub = pubSub; }

        public  void    DoSomethingElse(DateTime when)
        {
            var randomizer  = new Random();
            this.pubSub.Broadcast(new SomeOtherMessageType(){SomeAmount = randomizer.NextDouble(), SomeDate = when});
        }

    }

    public  class   SomeListener
    {

        public  void    Listen(PubSub<SomeMessageType> pubSub)
        {
            pubSub.Listen(this.SomeMessageEvent);
        }

        private void    SomeMessageEvent(SomeMessageType message)
        {
            Console.WriteLine("Attention! SomeMessageType receieved by SomeListener with\r\nid: {0}\r\ndescription: {1}\r\n", message.SomeId, message.SomeDescription);
        }

    }

    public  class   SomeOtherListener1
    {

        public  void    Listen(PubSub<SomeOtherMessageType> pubSub)
        {
            pubSub.Listen(this.SomeMessageEvent);
        }

        private void    SomeMessageEvent(SomeOtherMessageType message)
        {
            Console.WriteLine("Heads up! SomeOtherMessageType receieved by SomeOtherListener1 with\r\namount: {0}\r\ndate: {1}\r\n", message.SomeAmount, message.SomeDate);
        }

    }

    public  class   SomeOtherListener2
    {

        public  void    Listen(PubSub<SomeOtherMessageType> pubSub)
        {
            pubSub.Listen(this.SomeMessageEvent);
        }

        private void    SomeMessageEvent(SomeOtherMessageType message)
        {
            Console.WriteLine("Yo! SomeOtherMessageType receieved by SomeOtherListener2 withr\namount: {0}\r\ndate: {1}\r\n", message.SomeAmount, message.SomeDate);
        }

    }

}