c#泛型函数类型不适应重载

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不应该工作),但是当类型TComponent?

我最终设法让它与:

if(component as Component != null){...}
Run Code Online (Sandbox Code Playgroud)

但我的问题是,为什么这是必要的?c# 不检查传递给泛型函数的类型的运算符重载吗?传递的文字类型肯定会在运行时换入吗?

V0l*_*dek 5

你是对的,实际的类型参数是在运行时交换的。

但是操作符调用在编译时是静态绑定的。运算符不是类型的实例方法,类型上定义了静态方法。所以代码如下:

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都将具有这些方法。但它不能假设任何其他内容,因为用户完全有可能将实际提供objectT. 所以代码如下:

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并传递一些不可分配ComponentFoo. 泛型方法只为所有引用类型编译一次,然后与有关实际类型参数的元数据一起重用

但是你可以通过说帮助编译器

Foo<T>(T a) where T : Component {
  ...
}
Run Code Online (Sandbox Code Playgroud)

现在您将 的可能值限制T为 的子类型Component,因此编译器很高兴地发出对Component的相等性检查的调用。如果您将此约束添加到您的方法中,它将解决您的问题。

从你对“模板”这个词的使用来看,你可能有 C++ 背景:关于泛型需要注意的重要一点是它们与 C++ 模板非常不同,特别是,如上所述,它们被编译一次(用于引用类型)所以其中的代码必须使用 的任何可能值T。它不会在每次调用时重新编译泛型方法中的代码(如 C++ 中的模板实例化)。

======

1请注意,名称op_Equality对用户不可见,您不能从 C# 代码中逐字调用该方法,您需要使用==运算符。