简体   繁体   中英

Problem with AOT (ahead of time) in a generic method after built to UWP and Il2CPP

I have a generic method for trying to get a generic value from a dictionary via key using my own TryGetValue like (of course cropped a lot to the essential)

public class BaseExample
{
    public virtual bool TryGetValue<T>(string key, out T value)
    {
        value = default;
        return false;
    }
}

public class Example : BaseExample
{
    private Dictionary<string, Item> _items = new Dictionary<string, Item>();

    public override bool TryGetValue<T>(string key, out T value)
    {
        value = default;
        if (!GetItem(key, value, out var setting)) { return false; }

        value = (T)setting.Value;
        return true;
    } 

    private bool GetItem<T>(string key, T value, out Item item)
    {
        item = null;

        if (!_items.ContainsKey(key))
        {
            item = null;
            return false;
        }

        item = _items[key];

        return true;
    }
}

This works in the Unity Editor but as soon as this is built to UWP and IL2CPP as soon as I try to run the method with eg

var value = example.TryGetValue<int>("SomeKey");

It throws a

 System.ExecutionEngineException: Attempting to call method 'Example::TryGetValue<System.Int32>' for which no ahead of time (AOT) code was generated.

What could be the reason for this and how can I fix it?

For my specific case (if it helps anyone) I was using

WrapperMethod<WeatherFrameInfo[]>();

WrapperMethod<T>() {
    JsonConvert.DeserializeObject<T>(json);
}

Which lead to the error:

2022/03/17 14:53:32.888 13468 13524 Error Unity ExecutionEngineException: Attempting to call method 'System.Collections.Generic.List`1[[WeatherFrameInfo, ScriptsAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::.cctor' for which no ahead of time (AOT) code was generated.
2022/03/17 14:53:32.888 13468 13524 Error Unity   at System.Reflection.RuntimeConstructorInfo.InternalInvoke (System.Object obj, System.Object[] parameters, System.Boolean wrapExceptions) [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Utilities.LateBoundReflectionDelegateFactory+<>c__DisplayClass5_0`1[T].<CreateDefaultConstructor>b__1 () [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Serialization.JsonArrayContract.CreateTemporaryCollection () [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewList (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonArrayContract contract, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtons

I theorized that Newtonsoft is converting my array input to a List at some point so I refactored my code to do that ahead of time and violá

WrapperMethod<List<WeatherFrameInfo>>();

WrapperMethod<T>() {
    JsonConvert.DeserializeObject<T>(json);
}

Ok after some further research and tests the conclusion to why this happened is as follows:

Since according method was part of an interface and only either called via interface or via the base class the Example.TryGetValue method was never explicitly called via this class.

Since the Example class lives in a different Assembly than the interface and Base class the code for Example.TryGetValue was somehow stripped of on compile time (since the compiler thinks it is never used anyway) and therefore missing later in the build.


I now know the reason and what to have in mind for the future but not yet a real solution except having an explicit implementation and calling that so it is not stripped of...

To work around an AOT issue like this, you can force the compiler to generate the proper code. You can add the following example method in your class:

public void UsedOnlyForAOTCodeGeneration() 
{
    // YOUR CODE HERE

    // Include an exception so we can be sure to know if this method is ever called.
    throw new InvalidOperationException("This method is used for AOT code generation only. 
    Do not call it at runtime.");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM