File: src\SignalR\common\Shared\ReflectionHelper.cs
Web Access
Project: src\src\SignalR\server\Core\src\Microsoft.AspNetCore.SignalR.Core.csproj (Microsoft.AspNetCore.SignalR.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Channels;
 
namespace Microsoft.AspNetCore.SignalR;
 
internal static class ReflectionHelper
{
    // mustBeDirectType - Hub methods must use the base 'stream' type and not be a derived class that just implements the 'stream' type
    // and 'stream' types from the client are allowed to inherit from accepted 'stream' types
    public static bool IsStreamingType(Type type, bool mustBeDirectType = false)
    {
        // TODO #2594 - add Streams here, to make sending files easy
 
        if (IsIAsyncEnumerable(type))
        {
            return true;
        }
 
        return TryGetStreamType(type, out _, mustBeDirectType);
    }
 
    public static bool TryGetStreamType(Type streamType, [NotNullWhen(true)] out Type? streamGenericType, bool mustBeDirectType = false)
    {
        Type? nullableType = streamType;
        do
        {
            if (nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(ChannelReader<>))
            {
                Debug.Assert(nullableType.GetGenericArguments().Length == 1);
 
                streamGenericType = nullableType.GetGenericArguments()[0];
                return true;
            }
 
            nullableType = nullableType.BaseType;
        } while (mustBeDirectType == false && nullableType != null);
 
        streamGenericType = null;
        return false;
    }
 
    public static bool IsIAsyncEnumerable(Type type) => GetIAsyncEnumerableInterface(type) is not null;
 
    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
        Justification = "The 'IAsyncEnumerable<>' Type must exist and so trimmer kept it. In which case " +
            "It also kept it on any type which implements it. The below call to GetInterfaces " +
            "may return fewer results when trimmed but it will return 'IAsyncEnumerable<>' " +
            "if the type implemented it, even after trimming.")]
    public static Type? GetIAsyncEnumerableInterface(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
        {
            return type;
        }
 
        foreach (Type typeToCheck in type.GetInterfaces())
        {
            if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
            {
                return typeToCheck;
            }
        }
 
        return null;
    }
}