7
7
using System . Collections . Generic ;
8
8
using System . ComponentModel ;
9
9
using System . Diagnostics ;
10
+ using System . Diagnostics . CodeAnalysis ;
10
11
using System . IO ;
11
12
#if ! NET
12
13
using System . Linq ;
25
26
#pragma warning disable CA1031 // Do not catch general exception types
26
27
#pragma warning disable S2333 // Redundant modifiers should not be used
27
28
#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields
28
- #pragma warning disable SA1202 // Public members should come before private members
29
29
30
30
namespace Microsoft . Extensions . AI ;
31
31
@@ -373,15 +373,16 @@ public static AIFunction Create(MethodInfo method, object? target, string? name
373
373
}
374
374
375
375
/// <summary>
376
- /// Creates an <see cref="AIFunction"/> instance for a method, specified via a <see cref="MethodInfo"/> for
377
- /// an instance method and a <see cref="Func{AIFunctionArguments,Object} "/> for constructing an instance of
378
- /// the receiver object each time the <see cref="AIFunction"/> is invoked.
376
+ /// Creates an <see cref="AIFunction"/> instance for a method, specified via an <see cref="MethodInfo"/> for
377
+ /// and instance method, along with a <see cref="Type "/> representing the type of the target object to
378
+ /// instantiate each time the method is invoked.
379
379
/// </summary>
380
380
/// <param name="method">The instance method to be represented via the created <see cref="AIFunction"/>.</param>
381
- /// <param name="createInstanceFunc">
382
- /// Callback used on each function invocation to create an instance of the type on which the instance method <paramref name="method"/>
383
- /// will be invoked. If the returned instance is <see cref="IAsyncDisposable"/> or <see cref="IDisposable"/>, it will be disposed of
384
- /// after <paramref name="method"/> completes its invocation.
381
+ /// <param name="targetType">
382
+ /// The <see cref="Type"/> to construct an instance of on which to invoke <paramref name="method"/> when
383
+ /// the resulting <see cref="AIFunction"/> is invoked. <see cref="Activator.CreateInstance(Type)"/> is used,
384
+ /// utilizing the type's public parameterless constructor. If an instance can't be constructed, an exception is
385
+ /// thrown during the function's invocation.
385
386
/// </param>
386
387
/// <param name="options">Metadata to use to override defaults inferred from <paramref name="method"/>.</param>
387
388
/// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns>
@@ -455,16 +456,22 @@ public static AIFunction Create(MethodInfo method, object? target, string? name
455
456
/// </para>
456
457
/// </remarks>
457
458
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
458
- /// <exception cref="ArgumentNullException"><paramref name="createInstanceFunc "/> is <see langword="null"/>.</exception>
459
+ /// <exception cref="ArgumentNullException"><paramref name="targetType "/> is <see langword="null"/>.</exception>
459
460
/// <exception cref="ArgumentException"><paramref name="method"/> represents a static method.</exception>
460
461
/// <exception cref="ArgumentException"><paramref name="method"/> represents an open generic method.</exception>
461
462
/// <exception cref="ArgumentException"><paramref name="method"/> contains a parameter without a parameter name.</exception>
463
+ /// <exception cref="ArgumentException"><paramref name="targetType"/> is not assignable to <paramref name="method"/>'s declaring type.</exception>
462
464
/// <exception cref="JsonException">A parameter to <paramref name="method"/> or its return type is not serializable.</exception>
463
465
public static AIFunction Create (
464
466
MethodInfo method ,
465
- Func < AIFunctionArguments , object > createInstanceFunc ,
466
- AIFunctionFactoryOptions ? options = null ) =>
467
- ReflectionAIFunction . Build ( method , createInstanceFunc , options ?? _defaultOptions ) ;
467
+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type targetType ,
468
+ AIFunctionFactoryOptions ? options = null )
469
+ {
470
+ _ = Throw . IfNull ( method ) ;
471
+ _ = Throw . IfNull ( targetType ) ;
472
+
473
+ return ReflectionAIFunction . Build ( method , targetType , options ?? _defaultOptions ) ;
474
+ }
468
475
469
476
private sealed class ReflectionAIFunction : AIFunction
470
477
{
@@ -495,11 +502,10 @@ public static ReflectionAIFunction Build(MethodInfo method, object? target, AIFu
495
502
496
503
public static ReflectionAIFunction Build (
497
504
MethodInfo method ,
498
- Func < AIFunctionArguments , object > createInstanceFunc ,
505
+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type targetType ,
499
506
AIFunctionFactoryOptions options )
500
507
{
501
508
_ = Throw . IfNull ( method ) ;
502
- _ = Throw . IfNull ( createInstanceFunc ) ;
503
509
504
510
if ( method . ContainsGenericParameters )
505
511
{
@@ -511,7 +517,13 @@ public static ReflectionAIFunction Build(
511
517
Throw . ArgumentException ( nameof ( method ) , "The method must be an instance method." ) ;
512
518
}
513
519
514
- return new ( ReflectionAIFunctionDescriptor . GetOrCreate ( method , options ) , createInstanceFunc , options ) ;
520
+ if ( method . DeclaringType is { } declaringType &&
521
+ ! declaringType . IsAssignableFrom ( targetType ) )
522
+ {
523
+ Throw . ArgumentException ( nameof ( targetType ) , "The target type must be assignable to the method's declaring type." ) ;
524
+ }
525
+
526
+ return new ( ReflectionAIFunctionDescriptor . GetOrCreate ( method , options ) , targetType , options ) ;
515
527
}
516
528
517
529
private ReflectionAIFunction ( ReflectionAIFunctionDescriptor functionDescriptor , object ? target , AIFunctionFactoryOptions options )
@@ -523,17 +535,20 @@ private ReflectionAIFunction(ReflectionAIFunctionDescriptor functionDescriptor,
523
535
524
536
private ReflectionAIFunction (
525
537
ReflectionAIFunctionDescriptor functionDescriptor ,
526
- Func < AIFunctionArguments , object > createInstanceFunc ,
538
+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type targetType ,
527
539
AIFunctionFactoryOptions options )
528
540
{
529
541
FunctionDescriptor = functionDescriptor ;
530
- CreateInstanceFunc = createInstanceFunc ;
542
+ TargetType = targetType ;
543
+ CreateInstance = options . CreateInstance ;
531
544
AdditionalProperties = options . AdditionalProperties ?? EmptyReadOnlyDictionary < string , object ? > . Instance ;
532
545
}
533
546
534
547
public ReflectionAIFunctionDescriptor FunctionDescriptor { get ; }
535
548
public object ? Target { get ; }
536
- public Func < AIFunctionArguments , object > ? CreateInstanceFunc { get ; }
549
+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ]
550
+ public Type ? TargetType { get ; }
551
+ public Func < Type , AIFunctionArguments , object > ? CreateInstance { get ; }
537
552
538
553
public override IReadOnlyDictionary < string , object ? > AdditionalProperties { get ; }
539
554
public override string Name => FunctionDescriptor . Name ;
@@ -550,12 +565,14 @@ private ReflectionAIFunction(
550
565
object ? target = Target ;
551
566
try
552
567
{
553
- if ( CreateInstanceFunc is { } func )
568
+ if ( TargetType is { } targetType )
554
569
{
555
570
Debug . Assert ( target is null , "Expected target to be null when we have a non-null target type" ) ;
556
571
Debug . Assert ( ! FunctionDescriptor . Method . IsStatic , "Expected an instance method" ) ;
557
572
558
- target = func ( arguments ) ;
573
+ target = CreateInstance is not null ?
574
+ CreateInstance ( targetType , arguments ) :
575
+ Activator . CreateInstance ( targetType ) ;
559
576
if ( target is null )
560
577
{
561
578
Throw . InvalidOperationException ( "Unable to create an instance of the target type." ) ;
@@ -1088,34 +1105,6 @@ public override void Flush()
1088
1105
{
1089
1106
}
1090
1107
1091
- public override Task FlushAsync ( CancellationToken cancellationToken ) =>
1092
- Task . CompletedTask ;
1093
-
1094
- public override Task WriteAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken ) =>
1095
- WriteAsync ( new ReadOnlyMemory < byte > ( buffer , offset , count ) , cancellationToken ) . AsTask ( ) ;
1096
-
1097
- #if NET
1098
- public override
1099
- #else
1100
- private
1101
- #endif
1102
- ValueTask WriteAsync ( ReadOnlyMemory < byte > buffer , CancellationToken cancellationToken = default )
1103
- {
1104
- EnsureNotDisposed ( ) ;
1105
-
1106
- if ( cancellationToken . IsCancellationRequested )
1107
- {
1108
- return new ValueTask ( Task . FromCanceled ( cancellationToken ) ) ;
1109
- }
1110
-
1111
- EnsureCapacity ( _position + buffer . Length ) ;
1112
-
1113
- buffer . Span . CopyTo ( _buffer . AsSpan ( _position ) ) ;
1114
- _position += buffer . Length ;
1115
-
1116
- return default ;
1117
- }
1118
-
1119
1108
public override int Read ( byte [ ] buffer , int offset , int count ) => throw new NotSupportedException ( ) ;
1120
1109
public override long Seek ( long offset , SeekOrigin origin ) => throw new NotSupportedException ( ) ;
1121
1110
public override void SetLength ( long value ) => throw new NotSupportedException ( ) ;
0 commit comments