在 iOS 上通过 C# IL2CPP 中的反射调用泛型方法
Invoke generic method via reflection in C# IL2CPP on iOS
这个问题专门针对 Unity3d IL2CPP 和 iOS。
使用反射调用泛型方法
class SourceValue<T> { public T value; }
class TargetValue<T> { public T value; }
static TargetValue<T> GenericMethod<T> (SourceValue<T> source) {
return new TargetValue<T> { value = source.value };
}
void Main () {
Type genericType = typeof(SourceValue<float>);
Type typeArg = genericType.GenericTypeArguments[0];
MethodInfo mi = GetType ().GetMethod ("GenericMethod", Flags | BindingFlags.Static);
MethodInfo gmi = mi.MakeGenericMethod (typeArg);
object src = new SourceValue<float> { value = 0.5f };
object trg = gmi.Invoke (this, new object[] { src });
}
运行 在 mac 上的 Unity 编辑器中,这按预期工作。 iOS 调用失败,错误为:
ExecutionEngineException: Attempting to call method 'GenericMethod<System.Single>' for which no ahead of time (AOT) code was generated.
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
仅仅是AOT系统不能调用泛型方法还是我遗漏了什么?
是的,因为 IL2CPP 是一个 ahead-of-time (AOT) 编译器,它只适用于编译时存在的代码。这里没有代码使用 "real" C# 源代码中的 GenericMethod<float>
,因此 IL2CPP 不知道生成相应的代码来使该实现工作。
这里真正的限制是泛型参数类型是float
,这是一个值类型。在这种情况下,您可以毫无问题地使用 string
(引用类型)。 IL2CPP 共享所有具有泛型参数的泛型类型的实现,该泛型参数是引用类型(例如 string
、object
等)。这是可能的,因为 C# 中的所有引用类型都具有相同的大小(IntrPtr.Size
,确切地说)。
所以这里的限制真的是two-fold:
- IL2CPP 只能生成编译时已知的代码
- 当类型参数是值类型时,此限制仅适用于泛型类型。
请注意,理论上 IL2CPP 也可以共享具有值类型泛型参数的泛型类型的实现,尽管尚未实现。
这个问题专门针对 Unity3d IL2CPP 和 iOS。
使用反射调用泛型方法
class SourceValue<T> { public T value; }
class TargetValue<T> { public T value; }
static TargetValue<T> GenericMethod<T> (SourceValue<T> source) {
return new TargetValue<T> { value = source.value };
}
void Main () {
Type genericType = typeof(SourceValue<float>);
Type typeArg = genericType.GenericTypeArguments[0];
MethodInfo mi = GetType ().GetMethod ("GenericMethod", Flags | BindingFlags.Static);
MethodInfo gmi = mi.MakeGenericMethod (typeArg);
object src = new SourceValue<float> { value = 0.5f };
object trg = gmi.Invoke (this, new object[] { src });
}
运行 在 mac 上的 Unity 编辑器中,这按预期工作。 iOS 调用失败,错误为:
ExecutionEngineException: Attempting to call method 'GenericMethod<System.Single>' for which no ahead of time (AOT) code was generated.
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
仅仅是AOT系统不能调用泛型方法还是我遗漏了什么?
是的,因为 IL2CPP 是一个 ahead-of-time (AOT) 编译器,它只适用于编译时存在的代码。这里没有代码使用 "real" C# 源代码中的 GenericMethod<float>
,因此 IL2CPP 不知道生成相应的代码来使该实现工作。
这里真正的限制是泛型参数类型是float
,这是一个值类型。在这种情况下,您可以毫无问题地使用 string
(引用类型)。 IL2CPP 共享所有具有泛型参数的泛型类型的实现,该泛型参数是引用类型(例如 string
、object
等)。这是可能的,因为 C# 中的所有引用类型都具有相同的大小(IntrPtr.Size
,确切地说)。
所以这里的限制真的是two-fold:
- IL2CPP 只能生成编译时已知的代码
- 当类型参数是值类型时,此限制仅适用于泛型类型。
请注意,理论上 IL2CPP 也可以共享具有值类型泛型参数的泛型类型的实现,尽管尚未实现。