Rhinobyte.Extensions.Reflection 8.0.0-preview.1

This is a prerelease version of Rhinobyte.Extensions.Reflection.
dotnet add package Rhinobyte.Extensions.Reflection --version 8.0.0-preview.1                
NuGet\Install-Package Rhinobyte.Extensions.Reflection -Version 8.0.0-preview.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rhinobyte.Extensions.Reflection" Version="8.0.0-preview.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Rhinobyte.Extensions.Reflection --version 8.0.0-preview.1                
#r "nuget: Rhinobyte.Extensions.Reflection, 8.0.0-preview.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Rhinobyte.Extensions.Reflection as a Cake Addin
#addin nuget:?package=Rhinobyte.Extensions.Reflection&version=8.0.0-preview.1&prerelease

// Install Rhinobyte.Extensions.Reflection as a Cake Tool
#tool nuget:?package=Rhinobyte.Extensions.Reflection&version=8.0.0-preview.1&prerelease                

Rhinobyte.Extensions.Reflection

NuGet version (Rhinobyte.Extensions.Reflection)

This library contains extensions for .Net reflection including assembly scanning/type discovery extensions and support for parsing IL (intermediate-language) instructions.

Assembly Scanning Extensions

The assembly scanning features contain helper types and methods for convenient re-usable code for discovering from a set of assemblies. The AssemblyScanner type supports a variety of filtering features and will return a scan result that can be used for efficient lookup of discovered types within given criteria like interface types or concrete types.

See the Rhinobyte.Extensions.DependencyInjection library for extensions that leverage the assembly scanning types to automatically register types against an IServiceCollection instance.

Intermediate Langauge Extensions

The IL (intermediate language) features provide helper types extension methods for parsing the IL bytes from a method body into a collection of strongly typed instruction objects. The parsed instructions can then be leveraged for advanced reflection logic such as determining if a specific member is referenced within the body of a target method.

Convenience types and extension methods to output a concise human readable description of the IL instructions are also provided.

Other Extensions

Extension methods for rendering type name and method signature information to a string that is more human readable and more closely resembles an actual code signature.

Type information convenience methods such as type.IsCompilerGenerated() and type.IsOpenGeneric().

Usage Examples

Assembly Scanner Example Usage:
using Rhinobyte.Extensions.Reflection.AssemblyScanning;
using System.Reflection;

public namespace ExampleLibrary
{
    public interface ISomething { }

    public class Something : ISomething { }

    public class SomethingToIgnore1 { }

    public class SomethingToIgnore2 { }

    // Attribute support for excluding types from the assembly scanning result
    [IgnoreAssemblyScanner]
    public class SomethingWeDontWantScanned1 { }

    public class SomethingWeDontWantScanned2 { }

    public class SomethingWeDontWantScanned3 { }

    public class SomethingWeDontWantScanned4 { }

    public class SomeService
    {
        public void DoSomethingWithReflection()
        {
            var assemblyScanner = AssemblyScanner.CreateDefault()
                .AddForType<SomeService>() // Add the ExampleLibrary assembly to the list of assemblies to scan
                .Add(typeof(SomeOtherLibrary).Assembly)
                .ExcludeType<SomethingWeDontWantScanned2>()
                .ExcludeTypes(new [] { typeof(SomethingWeDontWantScanned3), typeof(SomethingWeDontWantScanned4) })
                .AddTypeFilter((assemblyInclude, discoveredType, scanner, currentScanResult) =>
                {
                    // Return true to ignore type names containing ToIgnore
                    return discoveredType.Name.Contains("ToIgnore");
                });

            var scanResult = assemblyScanner.ScanAssemblies();

            // Iterate through all of the discovered, non-ignored types
            foreach (var type in scanResult.AllDiscoveredTypes) 
            {
                // ... something ...
            }

            // Iterate through just the interface types
            foreach (var interfaceType in scanResult.InterfaceTypes)
            {
                var implementations = scanResult.ConcreteTypes.Where(concreteType => interfaceType.IsAssignableFrom(concreteType)).ToList();
            }
        }
    }
}
IL Method Body Parsing Example Usage:
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhinobyte.Extensions.Reflection;
using Rhinobyte.Extensions.Reflection.AssemblyScanning;
using Rhinobyte.Extensions.Reflection.IntermediateLanguage;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace Something.SomeLegacyWinFormsApp.UnitTests
{
    [TestMethod]
    public class LegacyProjectTests
    {
        [TestMethod]
        public void Form_types_that_use_embedded_resources_can_resolve_the_resources_successfully()
        {
            var scanResult = AssemblyScanner.CreateDefault()
                .AddForType<Something.SomeLegacyWinFormsApp.Program>()
                .ScanAssemblies();

            var failedResources = new List<string>();
            foreach (var discoveredType in scanResult.ConcreteTypes)
            {
                if (!typeof(System.Windows.Forms.Form).IsAssignableFrom(discoveredType))
                    continue;
                
                var initializeComponentMethod = discoveredType.GetMethod("InitializeComponent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                initializeComponentMethod.Should().NotBeNull();

                initializeComponentMethodInstructions = initializeComponentMethod!.ParseInstructions();
                ComponentResourceManager? resourceManager = null;
                foreach (var ilInstruction in initializeComponentMethodInstructions)
                {
                    if (ilInstruction is not MethodReferenceInstruction methodReferenceInstruction)
                        continue;

                    if (!methodReferenceInstruction.MethodReference.DeclaringType.Name.Contains("ResourceManager")
                        || methodReferenceInstruction.MethodReference.Name != nameof(ComponentResourceManager.GetObject))
                        continue;

                    // Get the string instruction that serves as the parameter for the resources.GetObject(resourceName) call
                    var resourceNameInstruction = (StringInstruction)methodReferenceInstruction.PreviousInstruction!;
                    
                    if (resourceManager is null)
                        resourceManager = new ComponentResourceManager(discoveredType);

                    try
                    {
                        object resource = resourceManager.GetObject(resourceNameInstruction.Value);
                        resource.Should().NotBeNull();
                    }
                    catch (Exception)
                    {
                        failedResources.Add($"{discoveredType.FullName}.resource -> {resourceName}");
                    }
                }
            }

            if (failedResources.Count > 0)
                Assert.Fail($"The following resources we're not found. Make sure the resource file/item exists and that the Form namespace matches the .resx file/folder structure:{Environment.NewLine}{string.Join(Environment.NewLine, failedResources)}");
        }
    }
}
Misc Extension Examples
using FluentAssertions;
using Rhinobyte.Extensions.Reflection;
using System.Reflection;

namespace SomeNamespace
{
    [TestClass]
    public class ExtensionExamples
    {
        [TestMethod]
        public void GetSignature_example_test()
        {
            var tryGetSomethingMethod = typeof(SomeClass<>).GetMethods().FirstOrDefault(method => method.Name == "TryGetSomething");

            // .ToString() vs .GetSignature() extension method
            tryGetSomethingMethod.ToString().Should().Be(
            "System.Nullable`1[T] TryGetSomething()");

            tryGetSomethingMethod.GetSignature().Should().Be(
            "public T? TryGetSomething()");

            tryGetSomethingMethod.GetSignature(useFullTypeName: true).Should().Be(
            "public T? SomeNamespace.GenericStruct<T>.TryGetSomething() where T : System.IConvertible, struct");
        }
    }

    public struct GenericStruct<T>
        where T : struct, IConvertible
    {
        public T? TryGetSomething() { /** code **/ }
    }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Rhinobyte.Extensions.Reflection:

Package Downloads
Rhinobyte.Extensions.DependencyInjection

Extensions for the .NET dependency injection libraries providing convention based auto-registration support The Rhinobyte.Extensions.Reflection.AssemblyScanner can be used to configure which assemblies to scan and which types to include/exclude. The IServiceCollection extension methods provide support for registering auto-discovered types and for extending registration to support constructor selection.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
8.0.0-preview.1 35 8/4/2024
1.0.2 2,691 10/3/2021
1.0.1 331 10/1/2021
1.0.0 429 9/29/2021

v8.0.0-preview.1
- Updated target frameworks to .NET 8.0, .NET 6.0, .NET 48, .NETSTANDARD 2.1, and .NETSTANDARD 2.0
- Updated microsoft nuget package dependencies to 8.0 versions
- Updated package version pattern to match .NET standard versioning pattern
- Added a RecursiveInstructionFormatter implementation of IInstructionFormatter