Skip to content

Commit

Permalink
Optimize generic MethodInfo for Func<T, TResult>
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Feb 14, 2020
1 parent d7e1abd commit 470f833
Show file tree
Hide file tree
Showing 29 changed files with 526 additions and 80 deletions.
90 changes: 90 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncInstanceMethodInfo1`3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
/// <item><description>One generic type argument.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal sealed class FuncInstanceMethodInfo1<TTarget, T, TResult> : FuncMethodInfo1<T, TResult>
where TTarget : class
{
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";

public FuncInstanceMethodInfo1(Func<T, TResult> function)
: this(function.Method)
{
}

private FuncInstanceMethodInfo1(MethodInfo methodInfo)
: base(methodInfo)
{
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
}

/// <summary>
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, T, TResult}"/> representing the <see cref="MethodInfo"/>
/// for a generic instance method. This helper method allows the instance to be created prior to the creation of
/// any instances of the target type. The following example shows the creation of an instance representing the
/// <see cref="object.Equals(object)"/> method:
///
/// <code>
/// FuncInstanceMethodInfo1&lt;object, object, int&gt;.Create(obj => obj.Equals)
/// </code>
/// </summary>
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, T, TResult}"/> representing the <see cref="MethodInfo"/>
/// for the generic instance method.</returns>
public static FuncInstanceMethodInfo1<TTarget, T, TResult> Create(Expression<Func<TTarget, Func<T, TResult>>> expression)
{
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
{
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
}

// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");

// Verify that we are creating a delegate of type Func<T, TResult>
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<T, TResult>), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");

// Check the MethodInfo
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");

var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");

return new FuncInstanceMethodInfo1<TTarget, T, TResult>(methodInfo);
}
}
}
90 changes: 90 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncInstanceMethodInfo3`3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
/// <item><description>Three generic type arguments.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal sealed class FuncInstanceMethodInfo3<TTarget, T, TResult> : FuncMethodInfo3<T, TResult>
where TTarget : class
{
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";

public FuncInstanceMethodInfo3(Func<T, TResult> function)
: this(function.Method)
{
}

private FuncInstanceMethodInfo3(MethodInfo methodInfo)
: base(methodInfo)
{
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
}

/// <summary>
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, T, TResult}"/> representing the <see cref="MethodInfo"/>
/// for a generic instance method. This helper method allows the instance to be created prior to the creation of
/// any instances of the target type. The following example shows the creation of an instance representing the
/// <see cref="object.Equals(object)"/> method:
///
/// <code>
/// FuncInstanceMethodInfo1&lt;object, object, int&gt;.Create(obj => obj.Equals)
/// </code>
/// </summary>
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, T, TResult}"/> representing the <see cref="MethodInfo"/>
/// for the generic instance method.</returns>
public static FuncInstanceMethodInfo3<TTarget, T, TResult> Create(Expression<Func<TTarget, Func<T, TResult>>> expression)
{
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
{
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
}

// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");

// Verify that we are creating a delegate of type Func<T, TResult>
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<T, TResult>), nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");

// Check the MethodInfo
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");

var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");

return new FuncInstanceMethodInfo3<TTarget, T, TResult>(methodInfo);
}
}
}
47 changes: 47 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncMethodInfo1`2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>One generic type argument.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal abstract class FuncMethodInfo1<T, TResult> : FuncMethodInfo<T, TResult>
{
private ImmutableDictionary<Type, MethodInfo> _instanceMethodInfo;

private protected FuncMethodInfo1(MethodInfo methodInfo)
: base(methodInfo)
{
_instanceMethodInfo = ImmutableDictionary<Type, MethodInfo>.Empty;

Contracts.CheckParam(GenericMethodDefinition.GetGenericArguments().Length == 1, nameof(methodInfo),
"Should have exactly one generic type parameter but does not");
}

public MethodInfo MakeGenericMethod(Type typeArg1)
{
return ImmutableInterlocked.GetOrAdd(
ref _instanceMethodInfo,
typeArg1,
(typeArg, methodInfo) => methodInfo.MakeGenericMethod(typeArg),
GenericMethodDefinition);
}
}
}
47 changes: 47 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncMethodInfo3`2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>Three generic type arguments.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal abstract class FuncMethodInfo3<T, TResult> : FuncMethodInfo<T, TResult>
{
private ImmutableDictionary<(Type, Type, Type), MethodInfo> _instanceMethodInfo;

private protected FuncMethodInfo3(MethodInfo methodInfo)
: base(methodInfo)
{
_instanceMethodInfo = ImmutableDictionary<(Type, Type, Type), MethodInfo>.Empty;

Contracts.CheckParam(GenericMethodDefinition.GetGenericArguments().Length == 3, nameof(methodInfo),
"Should have exactly three generic type parameters but does not");
}

public MethodInfo MakeGenericMethod(Type typeArg1, Type typeArg2, Type typeArg3)
{
return ImmutableInterlocked.GetOrAdd(
ref _instanceMethodInfo,
(typeArg1, typeArg2, typeArg3),
(args, methodInfo) => methodInfo.MakeGenericMethod(args.Item1, args.Item2, args.Item3),
GenericMethodDefinition);
}
}
}
26 changes: 26 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncMethodInfo`2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
internal abstract class FuncMethodInfo<T, TResult>
{
private protected FuncMethodInfo(MethodInfo methodInfo)
{
Contracts.CheckValue(methodInfo, nameof(methodInfo));

Contracts.CheckParam(methodInfo.IsGenericMethod, nameof(methodInfo), "Should be generic but is not");

GenericMethodDefinition = methodInfo.GetGenericMethodDefinition();
Contracts.CheckParam(typeof(TResult).IsAssignableFrom(GenericMethodDefinition.ReturnType), nameof(methodInfo), "Cannot be generic on return type");
}

protected MethodInfo GenericMethodDefinition { get; }
}
}
33 changes: 33 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncStaticMethodInfo1`2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>The method is static.</description></item>
/// <item><description>One generic type argument.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal sealed class FuncStaticMethodInfo1<T, TResult> : FuncMethodInfo1<T, TResult>
{
public FuncStaticMethodInfo1(Func<T, TResult> function)
: base(function.Method)
{
Contracts.CheckParam(GenericMethodDefinition.IsStatic, nameof(function), "Should be a static method");
}
}
}
33 changes: 33 additions & 0 deletions src/Microsoft.ML.Core/Utilities/FuncStaticMethodInfo3`2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Reflection;
using Microsoft.ML.Runtime;

namespace Microsoft.ML.Internal.Utilities
{
/// <summary>
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T, TResult}"/>,
/// with the following characteristics:
///
/// <list type="bullet">
/// <item><description>The method is static.</description></item>
/// <item><description>Three generic type arguments.</description></item>
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
/// </list>
/// </summary>
/// <typeparam name="T">The type of the parameter of the method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
internal sealed class FuncStaticMethodInfo3<T, TResult> : FuncMethodInfo3<T, TResult>
{
public FuncStaticMethodInfo3(Func<T, TResult> function)
: base(function.Method)
{
Contracts.CheckParam(GenericMethodDefinition.IsStatic, nameof(function), "Should be a static method");
}
}
}
Loading

0 comments on commit 470f833

Please sign in to comment.