簡體   English   中英

了解泛型和Func參數

[英]Understanding generics and Func arguments

如果在某個地方回答了這個問題,我不會感到驚訝,問題是我不確定如何用短語表達搜索以找到所需的內容。 我已經發現的東西要么過於簡單以至於無法使用,要么解釋不清,以致於我無法將其轉化為自己的項目。 我沒有事件處理程序,委托等類似的正式指導(哎呀,直到我大學畢業並被聘為程序員之前,我什至沒有了解實體組件系統或其他設計模式),即便如此,這也不是我在工作中或為工作而學到的東西。

基本上我想知道的是,是什么的Array.Sort的定義<T>(T []數組,比較<T>比較) 看起來像什么?

顯然,正在進行某種概括,因為myCompareDelegate(...)接受兩個任意類型的參數。 在我發現的與Func參數有關的幾乎所有內容中,Func <>參數都需要顯式聲明的類型,但某些示例代碼使用的是我不熟悉的運算符:

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

它可以編譯,但是我不知道它是做什么的,因此,我不知道如何利用它來創建將運行或執行我想做的代碼。

我在這里的目標是能夠創建一個事件系統(是的,我知道C#內置了一個事件系統,但是同樣,我所看到的所有示例代碼都被簡化到無用的地步-偵聽器與調度程序包含在同一類中-或復雜且無法解釋)。 我希望以下內容是正確的:

  1. 一個用於注冊事件偵聽器的函數(適用於任何類型的事件及其子類型)
  2. 一個用於調度事件的函數(僅調用相關的偵聽器)
  3. 能夠創建新事件類型而無需修改注冊和處理功能(在分派器中沒有超出基本事件類的顯式類型),只要新事件類型擴展了允許的事件類型(即,實體將僅分派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())查詢該對象以檢索偵聽器列表,然后調用它們:listItem(evt)

但是我寧願能夠做到這一點:

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

但是我無法終生解決該問題,因為我不知道如何以Array.Sort <T>(T [] array,Compare的方式聲明attachEvent()函數以采用通用函數參數<T>比較)。 我得到錯誤:

“無法從用法中推斷方法doSomething <T>(SomeClass.Thing <T>)'的類型參數。嘗試顯式指定類型參數。”

我認為您可能正在尋找以下內容:

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

    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM