在iOS上的C#IL2CPP中通过反射调用通用方法

时间:2019-05-17 09:35:06

标签: c# ios unity3d reflection 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系统不能调用通用方法还是我遗漏了什么?

1 个答案:

答案 0 :(得分:3)

是的,因为IL2CPP是一个提前(AOT)编译器,所以它仅适用于编译时存在的代码。这里没有代码使用“真实” C#源代码中的GenericMethod<float>,因此IL2CPP不知道生成相应的代码来使该实现起作用。

这里真正的限制是泛型参数类型为float,这是一个值类型。在这种情况下,您可以使用string(引用类型)而不会出现任何问题。 IL2CPP共享所有具有泛型参数的泛型类型的实现,该泛型参数是引用类型(例如stringobject等)。这是可能的,因为C#中的所有引用类型都具有相同的大小(准确地说是IntrPtr.Size)。

所以这里的限制实际上有两个方面:

  1. IL2CPP只能生成在编译时知道的代码
  2. 当type参数为值类型时,此限制仅适用于泛型类型。

请注意,尽管还没有实现,但从理论上讲,IL2CPP也可以使用值类型泛型参数共享泛型类型的实现。