diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs index eb2d6757..230bc304 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs @@ -101,6 +101,7 @@ namespace Spring.Reflection.Dynamic #endregion private readonly FunctionDelegate method; + private readonly object[] nullArguments; /// /// Creates a new instance of the safe method wrapper. @@ -112,6 +113,7 @@ namespace Spring.Reflection.Dynamic this.methodInfo = methodInfo; this.method = GetOrCreateDynamicMethod(methodInfo); + this.nullArguments = new object[methodInfo.GetParameters().Length]; } /// @@ -126,8 +128,15 @@ namespace Spring.Reflection.Dynamic /// /// A method return value. /// - public object Invoke(object target, object[] arguments) + public object Invoke(object target, params object[] arguments) { + // special case - when calling Invoke(null,null) it is undecidible if the second null is an argument or the argument array + if (arguments==null && nullArguments.Length==1) arguments=nullArguments; + AssertUtils.IsTrue( + nullArguments.Length == (arguments==null?0:arguments.Length) + , string.Format("Invalid number of arguments passed into method {0} - expected {1}, but was {2}", methodInfo.Name, nullArguments.Length, arguments.Length) + ); + return this.method(target, arguments); } #else diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs index 6320876a..1a847528 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs @@ -698,14 +698,22 @@ namespace Spring.Reflection.Dynamic /// public static object ConvertValueTypeArgumentIfNecessary( object value, Type targetType, int argIndex ) { - // targetType is guaranteed to be a ValueType! if (value == null) { + if (ReflectionUtils.IsNullableType(targetType)) + { + return null; + } throw new InvalidCastException( string.Format( "Cannot convert NULL at position {0} to argument type {1}", argIndex, targetType.FullName ) ); } Type valueType = value.GetType(); - +#if NET_2_0 + if (ReflectionUtils.IsNullableType( targetType )) + { + targetType = Nullable.GetUnderlyingType(targetType); + } +#endif // no conversion necessary? if (valueType == targetType) { diff --git a/src/Spring/Spring.Core/Util/ReflectionUtils.cs b/src/Spring/Spring.Core/Util/ReflectionUtils.cs index 670cbf38..b1c97647 100644 --- a/src/Spring/Spring.Core/Util/ReflectionUtils.cs +++ b/src/Spring/Spring.Core/Util/ReflectionUtils.cs @@ -54,7 +54,19 @@ namespace Spring.Util public const BindingFlags AllMembersCaseInsensitiveFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static - | BindingFlags.IgnoreCase; + | BindingFlags.IgnoreCase; + + /// + /// Checks, if the specified type is a nullable + /// + public static bool IsNullableType( Type type ) + { +#if NET_2_0 + return (type.IsGenericType && type.GetGenericTypeDefinition()==typeof(Nullable<>)); +#else + return false; +#endif + } /// /// Returns signature for the specified , method name and argument diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs index e48f5392..1e52a4d2 100644 --- a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs @@ -108,6 +108,37 @@ namespace Spring.Reflection.Dynamic Assert.IsFalse((bool) isNullOrEmpty.Invoke(null, new object[] { "Ana Maria" })); } +#if NET_2_0 + [Test] + public void PassNullableArguments() + { + IDynamicMethod dm = DynamicMethod.Create(typeof(TestMethods).GetMethod("PassNullableArgumentStatic")); + DateTime dt = DateTime.Now; + + Assert.AreEqual(dt, dm.Invoke(null, dt)); + } +#endif + [Test] + public void PassInvalidNumberOfArguments() + { + IDynamicMethod dm = DynamicMethod.Create(typeof(TestMethods).GetMethod("PassNullableArgumentStatic")); + DateTime dt = DateTime.Now; + + Assert.IsNull(dm.Invoke(null, null)); // this is ok + try + { + dm.Invoke( null ); // this is not ok + Assert.Fail(); + } + catch (ArgumentException) { } + try + { + dm.Invoke( null, null, null ); // this is not ok + Assert.Fail(); + } + catch (ArgumentException) { } + } + [Test] public void TestArgumentTypeCasts() { @@ -225,7 +256,29 @@ namespace Spring.Reflection.Dynamic } #endregion - + + #region Helper Classes + +#if NET_2_0 + class TestMethods + { + public static object Invoke( object target, object[] args ) + { + return PassNullableArgumentStatic( (DateTime?)(args[0]) ); + } + + public DateTime? PassNullableArgument( DateTime? arg ) + { + return PassNullableArgumentStatic( arg ); + } + + public static DateTime? PassNullableArgumentStatic( DateTime? arg ) + { + return arg; + } + } +#endif + #endregion } #region IL generation helper classes (they help if you look at them in Reflector ;-)