理解泛型和函数参数
本文关键字:函数 参数 泛型 | 更新日期: 2023-09-27 18:04:01
如果这个问题在某个地方得到了回答,我不会感到惊讶,问题是我不确定如何表达搜索以找到我需要的东西。我已经发现的东西要么过于简单而无法使用,要么解释得很糟糕,以至于我无法将其转化为我自己的项目。我没有接受过关于事件处理程序、委托等方面的正式指导(我甚至没有学习过实体组件系统——或其他设计模式——直到我大学毕业后很久才被聘为程序员,即使在那时,这也不是我在工作中或为工作而学习的东西)。
本质上我想知道的是,array . sort
显然有某种泛化正在进行,因为myCompareDelegate(…)接受any类型的两个参数。在我发现的几乎所有与Func参数相关的内容中,Func<>形参都需要显式声明类型,除了一些使用我不熟悉的操作符的示例代码:
SomeUtility(arg => new MyType());
public void SomeUtility<T>(Func<object, T> converter) {
var myType = converter("foo");
}
它编译,但我不知道它做什么,因此,我不知道如何利用它来创建将运行或做我想做的代码。
我的目标是能够创建一个事件系统(是的,我知道c#有一个内置的事件系统,但是,我看到的所有示例代码要么被简化到无用的程度——侦听器包含在与调度程序相同的类中——要么复杂且无法解释)。我希望以下内容为真:
- 一个注册事件监听器的函数(用于任何类型的事件及其子类型)
- 分派事件的单一函数(只调用相关的监听器)
- 能够创建新的事件类型,而无需修改注册和处理的函数(除了基事件类之外,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
"方法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);
}
}
}