Bug 3056 - Reflection of generic method throws an System.Reflection.TargetInvocationException (In Simulator no issue)
Summary: Reflection of generic method throws an System.Reflection.TargetInvocationExce...
Status: RESOLVED INVALID
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 5.2
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2012-01-25 09:21 UTC by jodur
Modified: 2012-01-26 11:10 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED INVALID

Description jodur 2012-01-25 09:21:20 UTC
public static void DistributeEvent(AdsNotification notification)
		{
		//Re-Raise event to Generic Twincatvar that was changed	
		var method = typeof(TwincatDevice).GetMethod("InvokeEventTwincatVar");
        var generic = method.MakeGenericMethod(notification.TypeOfValue);
		object[] args =	{notification};
	    generic.Invoke(null, args);
			
		}	
		
public static void InvokeEventTwincatVar<T>(AdsNotification notification) where T : IConvertible
		{	
		//Helper function to invoke the event for the variable that has changed	
		((TwincatVar<T>)notification.UserData).OnDataChangeEvent((T)(notification.Value));
		}	


Is have a method DistributeEvent that is called within a eventhandler and distrubutes the event to a underlying object that is passed through the AdsNotification object. With the Generic Helper function of InvokeEventTwincatVar<T> i reraise the event for the object that it belongs to.

After making the call: i get an System.Reflection.TargetInvocationException.

The same code on the simulator runs without any problem!

I resolved it for now with a replacement for my method (see below), but this is not as beautyfull as the generic invoke method!!



public static void DistributeEvent(AdsNotification notification)
		{	
		 	switch (Type.GetTypeCode(notification.TypeOfValue))
            {
                case TypeCode.Boolean: ((TwincatVar<bool>)notification.UserData).OnDataChangeEvent((bool)(notification.Value)); break;
                case TypeCode.Byte: ((TwincatVar<byte>)notification.UserData).OnDataChangeEvent((byte)(notification.Value)); break;
                case TypeCode.Int16:((TwincatVar<short>)notification.UserData).OnDataChangeEvent((short)(notification.Value)); break;
                case TypeCode.Int32: ((TwincatVar<int>)notification.UserData).OnDataChangeEvent((int)(notification.Value)); break;
				case TypeCode.Single:((TwincatVar<float>)notification.UserData).OnDataChangeEvent((float)(notification.Value)); break;
				case TypeCode.Double:((TwincatVar<double>)notification.UserData).OnDataChangeEvent((double)(notification.Value)); break;
                case TypeCode.UInt16:((TwincatVar<ushort>)notification.UserData).OnDataChangeEvent((ushort)(notification.Value)); break;
                case TypeCode.UInt32:((TwincatVar<uint>)notification.UserData).OnDataChangeEvent((uint)(notification.Value)); break;
                case TypeCode.String:((TwincatVar<string>)notification.UserData).OnDataChangeEvent((string)(notification.Value)); break;
            }
		}
Comment 1 Rolf Bjarne Kvinge [MSFT] 2012-01-25 19:02:29 UTC
This is unfortunately by design.

When our AOT compiler compiles for the device, it needs to know every single type in advance (since it is not possible to jit code on the device). This means that you can't construct generic types on the fly using reflection, the AOT compiler doesn't know which types you'll end up creating.

For more information read this:
http://docs.xamarin.com/ios/about/limitations
Comment 2 jodur 2012-01-26 02:46:14 UTC
Ok i already saw these limitations, but why isn't the following reflection code no problem on the device? ByteArrayToTypeValue is running without problems!

And why is it working om the simulator? and not on the device!


public static T GetResultFromBytes<T>(byte[] value) where T : IConvertible
        {
            object v = null;

            switch (Type.GetTypeCode(typeof(T)))
            {
                case TypeCode.Boolean: v = value[0]; break;
                case TypeCode.Byte: v = value[0]; break;
                case TypeCode.Int16: v = BitConverter.ToInt16(value, 0); break;
                case TypeCode.Int32: v = BitConverter.ToInt32(value, 0); break;
                case TypeCode.Int64: v = BitConverter.ToInt64(value, 0); break;
                case TypeCode.UInt16: v = BitConverter.ToUInt16(value, 0); break;
                case TypeCode.UInt32: v = BitConverter.ToUInt32(value, 0); break;
                case TypeCode.UInt64: v = BitConverter.ToUInt64(value, 0); break;
				case TypeCode.Single: v = BitConverter.ToSingle(value, 0); break;
				case TypeCode.Double: v = BitConverter.ToDouble(value, 0); break;
                case TypeCode.String: v = ByteArrayToString(value); break; 
            }

            return (T)Convert.ChangeType(v, Type.GetTypeCode(typeof(T)), null);
        }


public static object ByteArrayToTypeValue(byte[] value, Type TypeOfValue)
        {
            if (TypeOfValue == null) return null;
            if (TypeOfValue == typeof(byte[])) return value;
            var method = typeof(GenericHelper).GetMethod("GetResultFromBytes");
            var generic = method.MakeGenericMethod(TypeOfValue);
            return generic.Invoke(null, new object[] {value});
        }
Comment 3 Rolf Bjarne Kvinge [MSFT] 2012-01-26 08:52:10 UTC
It works in the simulator because we don't use the AOT compiler then (it's a lot slower to compile).

This code doesn't work because the AOT compiler doesn't know that it has to create one GetResultFromBytes<int>, one GetResultFromBytes<long>, one GetResultFromBytes<byte>, etc. You don't use any of those generic instantiations directly in your code.
Comment 4 jodur 2012-01-26 08:57:38 UTC
Well the ByteArrayToTypeValue is magically working on the simulator and on the device! This is what i don't understand!
Comment 5 Rolf Bjarne Kvinge [MSFT] 2012-01-26 09:00:17 UTC
Oh, I misunderstood your comment.

You might be accessing GetResultFromBytes from somewhere else (in a way which makes the AOT compiler know which types to instantiate it with).
Comment 6 Miguel de Icaza [MSFT] 2012-01-26 11:10:40 UTC
One alternative that you can consider is statically referencing anything that you end up dynamically invoking by providing the exact type signature that will be used.

Like this:

[Preserve]
void EnsureMethodsAreCompiled ()
{
    GetResultFromBytes<int> (null);
    GetResultFromBytes<long> (null);
    GetResultFromBytes<byte> (null);
    GetResultFromBytes<ulong> (null);
}