Jay*_*Jay 0 c# generics unity-game-engine
我正在统一 C# 中编写一个通用函数(模板函数?),我遇到了这个问题。该函数是使用派生自的类型模板化运行的Component
T component = transform.gameObject.GetComponent<T>();
if(component != null)
{
objects.Add(component);
}
Run Code Online (Sandbox Code Playgroud)
我逐步完成了这个,它的值component是"null"(我期望的)但是然后程序继续并进入了if块......
在统一重载==/!=运算符以允许对其组件进行空检查(它们实际上永远不会为空 - 例如,component ??= foo不应该工作),但是当类型T为Component?
我最终设法让它与:
if(component as Component != null){...}
Run Code Online (Sandbox Code Playgroud)
但我的问题是,为什么这是必要的?c# 不检查传递给泛型函数的类型的运算符重载吗?传递的文字类型肯定会在运行时换入吗?
你是对的,实际的类型参数是在运行时交换的。
但是操作符调用在编译时是静态绑定的。运算符不是类型的实例方法,类型上定义了静态方法。所以代码如下:
Component a, b;
a == b
Run Code Online (Sandbox Code Playgroud)
决心
Component a, b;
Component.op_Equality(a, b)
Run Code Online (Sandbox Code Playgroud)
在编译时1 .
当你写一个通用的方法没有任何限制,编译器将T作为最一般的类型,可以这样:object。这允许您使用在 上定义的任何方法object,因为被替换的任何类型T都将具有这些方法。但它不能假设任何其他内容,因为用户完全有可能将实际提供object为T. 所以代码如下:
Foo<T>(T a) {
if (a == null) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
必须解决一个简单的空引用检查,因为您可以通过检查引用是否相等来比较任何object与。它无法解决null0
Foo<T>(T a) {
if (Component.op_Equality(a, null)) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
因为在运行时您可能会提供objectasT并传递一些不可分配Component给Foo. 泛型方法只为所有引用类型编译一次,然后与有关实际类型参数的元数据一起重用。
但是你可以通过说帮助编译器
Foo<T>(T a) where T : Component {
...
}
Run Code Online (Sandbox Code Playgroud)
现在您将 的可能值限制T为 的子类型Component,因此编译器很高兴地发出对Component的相等性检查的调用。如果您将此约束添加到您的方法中,它将解决您的问题。
从你对“模板”这个词的使用来看,你可能有 C++ 背景:关于泛型需要注意的重要一点是它们与 C++ 模板非常不同,特别是,如上所述,它们被编译一次(用于引用类型)所以其中的代码必须使用 的任何可能值T。它不会在每次调用时重新编译泛型方法中的代码(如 C++ 中的模板实例化)。
======
1请注意,名称op_Equality对用户不可见,您不能从 C# 代码中逐字调用该方法,您需要使用==运算符。