簡體   English   中英

將“this”傳遞給接受泛型參數的委托時,c# 編譯器錯誤

[英]c# compiler error when passing `this` to a delegate that accepts generic parameter

我想利用以下抽象 class 為統一項目創建事件通道。 我無法理解 generics 如何在 C# 中工作(這種語言對我來說是新的),並且在調用偵聽器時收到關於將this作為參數傳遞的編譯器錯誤。

namespace EventManagers
{
    public abstract class EventSubject<T> : MonoBehaviour
    {
        public delegate void Listener<T>(T eventSubject);

        private readonly 
            List<Listener<T>> _listeners = new List<Listener<T>>();

        public void Attach(Listener<T> listener)
        {
            _listeners.Add(listener);
        }

        public void Detach(Listener<T> listener)
        {
            _listeners.Remove(listener);
        }

        public void NotifyObservers()
        {
            foreach (Listener<T> listener in _listeners)
            {
                listener(this);
            }
        }
    }
}

引用讀取listener(this);

 error CS1503: Argument 1: cannot convert from 'EventManagers.EventSubject<T>' to 'T'

繼承 class 看起來像:

public class Selection : EventSubject<Selection> {
    private GameObject selected;
    private static Selection _instance;

    public static Selection instance
    {
        get
        {
            if (!_instance)
            {
                _instance = FindObjectOfType(typeof (Selection)) as Selection;
                if (!_instance) {
                    throw new Exception("You need a Selection in the scene");
                }
            }
            return _instance;
        }
    }

    
    public GameObject GetSelection() {
        return selected;
    }

    public void setSelection(GameObject selected) {
        this.selected = selected;
        NotifyObservers();
    }
}

我的問題是:

  1. 如果我的代表知道需要一個泛型類型,為什么會有問題?
  2. 我怎樣才能最好地實現這種事件模式?
public abstract class EventSubject<T> : MonoBehaviour { ... }
public class Selection : EventSubject<Selection> { ... }

從您的EventSubject class 的 POV 來看, typeof(T)可以是任何東西。 它甚至可以是EventSubject<int>因為您沒有提供任何where約束。 編譯器無法知道您期望typeof(T) == this.GetType()

您正在尋找的模式在 C++ 中被昵稱為 Curiously recurring template pattern 在 C# 中,等效的通用約束是;

public abstract class EventSubject<T> : MonoBehaviour 
    where T : EventSubject<T>
{
    ...
    public void NotifyObservers()
    {
        foreach (Listener listener in _listeners)
        {
            listener((T)this);
        }
    }
}

public class Selection : EventSubject<Selection> { ... }

這接近您想要的,因為它至少將T限制為擴展EventSubject<>的類。 但是編譯器仍然無法證明typeof(T) == this.GetType() ,因此您需要顯式轉換。

此約束還允許class Broken: EventSubject<Selection> ,並且沒有任何 C# 語言功能可以阻止它。 如果開發人員違反了您的typeof(T) == this.GetType()規則,您可以做的最好的事情就是運行時異常。

  1. Listener 期待Selection實例,您傳遞this是一個Listener<Selection>實例。
  2. 事件模式內置於 c# 語言 - https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events你基本上重新實現它沒有任何優勢。

對於可能正在觀看的其他人,我能夠取得工作成果。 我的 EventSubject class 現在顯示如下:

public abstract class EventSubject<TEventSubject> : MonoBehaviour
    {
        public delegate void Listener(TEventSubject eventSubject);

        private readonly
            List<Listener> _listeners = new List<Listener>();

        public void Attach(Listener listener)
        {
            _listeners.Add(listener);
        }

        public void Detach(Listener listener)
        {
            _listeners.Remove(listener);
        }

        public void NotifyObservers(TEventSubject eventSubject)
        {
            foreach (Listener listener in _listeners)
            {
                listener(eventSubject);
            }
        }
    }

我的Selection class 現在看起來像這樣:

public class Selection : EventSubject<Selection> {
    private GameObject selected;
    private static Selection _instance;

    public static Selection instance
    {
        get
        {
            if (!_instance)
            {
                _instance = FindObjectOfType(typeof (Selection)) as Selection;
                if (!_instance) {
                    throw new Exception("You need a Selection in the scene");
                }
            }
            return _instance;
        }
    }

    
    public GameObject GetSelection() {
        return selected;
    }

    public void setSelection(GameObject selected) {
        this.selected = selected;
        NotifyObservers(this);
    }
}

老實說,我從來沒有解決編譯器為什么不喜歡listener(this)的謎題,但能夠通過將其作為NotifyObservers的參數來解決這個問題。 我很想聽聽人們會改變的優化或其他事情。

暫無
暫無

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

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