理解泛型和函数参数

本文关键字:函数 参数 泛型 | 更新日期: 2023-09-27 18:04:01

如果这个问题在某个地方得到了回答,我不会感到惊讶,问题是我不确定如何表达搜索以找到我需要的东西。我已经发现的东西要么过于简单而无法使用,要么解释得很糟糕,以至于我无法将其转化为我自己的项目。我没有接受过关于事件处理程序、委托等方面的正式指导(我甚至没有学习过实体组件系统——或其他设计模式——直到我大学毕业后很久才被聘为程序员,即使在那时,这也不是我在工作中或为工作而学习的东西)。

本质上我想知道的是,array . sort (T[] array, comparon comparison) 的定义是什么样的?

显然有某种泛化正在进行,因为myCompareDelegate(…)接受any类型的两个参数。在我发现的几乎所有与Func参数相关的内容中,Func<>形参都需要显式声明类型,除了一些使用我不熟悉的操作符的示例代码:

SomeUtility(arg => new MyType());
public void SomeUtility<T>(Func<object, T> converter) {
    var myType = converter("foo");
}

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

我的目标是能够创建一个事件系统(是的,我知道c#有一个内置的事件系统,但是,我看到的所有示例代码要么被简化到无用的程度——侦听器包含在与调度程序相同的类中——要么复杂且无法解释)。我希望以下内容为真:

  1. 一个注册事件监听器的函数(用于任何类型的事件及其子类型)
  2. 分派事件的单一函数(只调用相关的监听器)
  3. 能够创建新的事件类型,而无需修改注册和处理的函数(除了基事件类之外,dispatcher中没有显式类型),前提是新的事件类型扩展了允许的事件类型(即实体只会调度EntityEvents而不是WorldEvents)。

我有一个系统,目前工作,但它要求我所有的处理程序通过一个单一的"onEvent"函数,该函数接受一个基本事件对象,并找出它的实际类型,传递给真正的处理程序。

,

//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) {}

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

但是我更愿意这样做:

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

但是我不知道如何做到这一点,因为我不知道如何像array . sort (T[] array, comparison comparison)那样声明attachEvent()函数以接受泛型函数参数。我得到错误:

"方法doSomething(SomeClass.Thing)'的类型参数不能从用法中推断出来。尝试显式指定类型参数。"

理解泛型和函数参数

我想你可能在寻找如下内容:

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,它不继承someemessagetype,并对它进行类似的调用,它只会广播到特定类型的侦听器。

编辑:

这是一个完整的概念证明,你可以在一个控制台应用程序中运行,以减轻你可能对这种技术的有效性的任何剩余担忧。

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);
        }
    }
}

EDITED AGAIN(使用基于实例的酒吧的替代概念证明):

这是一个使用基于实例的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);
        }
    }
}