[英]c# compiler error when passing `this` to a delegate that accepts generic parameter
I have the following abstract class that I would like to leverage to create event channels for a unity project.我想利用以下抽象 class 为统一项目创建事件通道。 I'm having trouble with understanding how generics work in C# (this language is new to me) and receiving a compiler error concerning passing
this
as an argument when invoking the listeners.我无法理解 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);
}
}
}
}
Error referring to the line that reads listener(this);
引用读取
listener(this);
: :
error CS1503: Argument 1: cannot convert from 'EventManagers.EventSubject<T>' to 'T'
An inheriting class looks like:继承 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();
}
}
My questions are:我的问题是:
public abstract class EventSubject<T> : MonoBehaviour { ... }
public class Selection : EventSubject<Selection> { ... }
From the POV of your EventSubject
class, typeof(T)
could be anything at all.从您的
EventSubject
class 的 POV 来看, typeof(T)
可以是任何东西。 It could even be EventSubject<int>
since you haven't provided any where
constraints.它甚至可以是
EventSubject<int>
因为您没有提供任何where
约束。 The compiler has no way of knowing that you expect typeof(T) == this.GetType()
.编译器无法知道您期望
typeof(T) == this.GetType()
。
The pattern you are looking for was nicknamed Curiously recurring template pattern in C++.您正在寻找的模式在 C++ 中被昵称为 Curiously recurring template pattern 。 In C#, the equivalent generic constraint is;
在 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> { ... }
This is close to what you want, as it at least limits T
to classes that extend EventSubject<>
.这接近您想要的,因为它至少将
T
限制为扩展EventSubject<>
的类。 But the compiler still can't prove that typeof(T) == this.GetType()
, so you need an explicit cast.但是编译器仍然无法证明
typeof(T) == this.GetType()
,因此您需要显式转换。
This constraint also allows class Broken: EventSubject<Selection>
, and there isn't any C# language feature that can prevent it.此约束还允许
class Broken: EventSubject<Selection>
,并且没有任何 C# 语言功能可以阻止它。 The best you can do is a runtime exception if a developer breaks your typeof(T) == this.GetType()
rule.如果开发人员违反了您的
typeof(T) == this.GetType()
规则,您可以做的最好的事情就是运行时异常。
Selection
instance, you're passing this
which is a Listener<Selection>
instance. Selection
实例,您传递this
是一个Listener<Selection>
实例。For others who may be viewing I was able to achieve a working result.对于可能正在观看的其他人,我能够取得工作成果。 My EventSubject class now appears as follows:
我的 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);
}
}
}
and my Selection
class now looks like this:我的
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);
}
}
I honestly never solved the riddle of why the compiler did not like listener(this)
but was able to work around that by making it an argument of NotifyObservers
.老实说,我从来没有解决编译器为什么不喜欢
listener(this)
的谜题,但能够通过将其作为NotifyObservers
的参数来解决这个问题。 I would love to hear optimizations or other things people would change.我很想听听人们会改变的优化或其他事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.