--- url: /reference/api.md --- # API Overview Complete reference of all public types, extension methods, and helpers in Cocoar.Reflectensions. ## Packages | Package | Namespace | Description | |---------|-----------|-------------| | [Cocoar.Reflectensions](/reference/packages#core) | `Cocoar.Reflectensions` | Core library — type parsing, reflection extensions, type conversion, utilities | | [Cocoar.Reflectensions.Invoke](/reference/packages#invoke) | `Cocoar.Reflectensions.Helper` | Dynamic method invocation with parameter matching | | [Cocoar.Reflectensions.ExpandableObject](/reference/packages#expandable-object) | `Cocoar.Reflectensions` | Dynamic objects with dictionary-like behavior | ## TypeHelper Static helper for parsing and resolving types from strings. ```csharp public static class TypeHelper { // Resolve type from string static Type? FindType(string typeName); static Type? FindType(string typeName, IDictionary customTypeMapping); // Normalize type name to CLR format static string NormalizeTypeName(string typeName); static string NormalizeTypeName(string typeName, IDictionary customTypeMapping); // Check base type static bool HasInspectableBaseType(Type type); } ``` ## ObjectReflection Readonly struct wrappers for object reflection. Created via `.Reflect()`. ```csharp // Non-generic wrapper (boxes value types) public readonly struct ObjectReflection : IObjectReflection { object? GetValue(); } // Generic wrapper (zero-cost for value types) public readonly struct ObjectReflection : IObjectReflection { T GetTypedValue(); object? GetValue(); static implicit operator ObjectReflection(ObjectReflection value); } ``` ## Extension Methods — Entry Point ```csharp public static class IObjectExtensions { // Generic — no boxing for value types static ObjectReflection Reflect(this T value); // Non-generic — for object-typed values static ObjectReflection Reflect(this object obj); } ``` ## Extension Methods — Type ```csharp public static class TypeExtensions { // Type checking static bool IsNumericType(this Type type); static bool IsGenericTypeOf(this Type type, Type genericType); static bool IsGenericTypeOf(this Type type); static bool IsNullableType(this Type type); static bool IsEnumerableType(this Type type); static bool IsDictionaryType(this Type type); static bool IsStatic(this Type type); static bool Equals(this Type type); static bool NotEquals(this Type type); // Relationships static bool ImplementsInterface(this Type type, Type interfaceType); static bool ImplementsInterface(this Type type); static bool InheritFromClass(this Type type); static bool InheritFromClass(this Type type, Type from); static bool InheritFromClass(this Type type, string from); static int InheritFromClassLevel(this Type type, Type from, int? maximumLevel = null); static int InheritFromClassLevel(this Type type, string from, int? maximumLevel = null); static int InheritFromClassLevel(this Type type, int? maximumLevel = null); // Castability static bool IsImplicitCastableTo(this Type type); static bool IsImplicitCastableTo(this Type type, Type to); // Attributes static bool HasAttribute(this Type type, bool inherit = false) where T : Attribute; static bool HasAttribute(this Type type, Type attributeType, bool inherit = false); // Operator methods static IEnumerable GetImplicitOperatorMethods(this Type type, bool throwOnError = true); static IEnumerable GetImplicitOperatorMethods(bool throwOnError = true); static IEnumerable GetExplicitOperatorMethods(this Type type, bool throwOnError = true); static IEnumerable GetExplicitOperatorMethods(bool throwOnError = true); static MethodInfo? GetImplicitCastMethodTo(this Type fromType); static MethodInfo? GetImplicitCastMethodTo(this Type fromType, Type toType); static MethodInfo? GetExplicitCastMethodTo(this Type fromType); static MethodInfo? GetExplicitCastMethodTo(this Type fromType, Type toType); // Interface discovery static IEnumerable GetDirectInterfaces(this Type type); } ``` ## Extension Methods — ObjectReflection ```csharp public static class ObjectReflectionExtensions { // Equality static bool EqualsToAnyOf(this IObjectReflection obj, params object[] equalsTo); static bool ToBoolean(this IObjectReflection obj, params object[] trueValues); // Castability static bool IsImplicitCastableTo(this IObjectReflection obj, Type type); static bool IsImplicitCastableTo(this IObjectReflection obj); // Type switching (As) static bool TryAs(this IObjectReflection obj, Type type, out object? outValue); static bool TryAs(this IObjectReflection obj, out T? outValue); static object? As(this IObjectReflection obj, Type type); static T? As(this IObjectReflection obj); // Smart conversion (To) static bool TryTo(this IObjectReflection obj, Type type, out object? outValue); static bool TryTo(this IObjectReflection obj, out T? outValue); static object? To(this IObjectReflection obj, Type type); static object? To(this IObjectReflection obj, Type type, object? defaultValue); static T? To(this IObjectReflection obj); static T? To(this IObjectReflection obj, T? defaultValue); // Hot path overloads for ObjectReflection, , , , , static TResult? To(this ObjectReflection obj); static TResult? To(this ObjectReflection obj); static TResult? To(this ObjectReflection obj); static TResult? To(this ObjectReflection obj); static TResult? To(this ObjectReflection obj); static TResult? To(this ObjectReflection obj); // Property access static object? GetPropertyValue(this IObjectReflection obj, string name, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance); static T? GetPropertyValue(this IObjectReflection obj, string name, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance); static void SetPropertyValue(this IObjectReflection obj, string path, object value, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance); } ``` ## Extension Methods — MethodInfo ```csharp public static class MethodInfoExtensions { static bool HasName(this MethodInfo m, string name, StringComparison comparison = StringComparison.CurrentCulture); static bool HasParametersLengthOf(this MethodInfo m, int length, bool includeOptional = false); static bool HasParametersOfType(this MethodInfo m, Type[] types); static bool HasAttribute(this MethodInfo m, bool inherit = false) where T : Attribute; static bool HasAttribute(this MethodInfo m, Type attributeType, bool inherit = false); static bool HasReturnType(this MethodInfo m); static bool HasReturnType(this MethodInfo m, Type returnType); static bool HasGenericArgumentsLengthOf(this MethodInfo m, int length); static bool HasGenericArguments(this MethodInfo m, Type[] types); } public static class MethodInfoEnumerableExtensions { static IEnumerable WithName(this IEnumerable methods, string name, StringComparison comparison = StringComparison.CurrentCulture); static IEnumerable WithReturnType(this IEnumerable methods); static IEnumerable WithReturnType(this IEnumerable methods, Type type); static IEnumerable WithParametersLengthOf(this IEnumerable methods, int length, bool includeOptional = false); static IEnumerable WithParametersOfType(this IEnumerable methods, params Type[] types); static IEnumerable WithGenericArgumentsLengthOf(this IEnumerable methods, int length); static IEnumerable WithGenericArgumentsOfType(this IEnumerable methods, params Type[] types); static IEnumerable WithAttribute(this IEnumerable methods, bool inherit = false) where T : Attribute; static IEnumerable<(MethodInfo, T)> WithAttributeExpanded(this IEnumerable methods, bool inherit = false) where T : Attribute; } public static class MethodBaseExtensions { static bool IsExtensionMethod(this MethodBase method); } ``` ## Extension Methods — Type Collections ```csharp public static class TypeEnumerableExtensions { static IEnumerable WithAttribute(this IEnumerable types, bool inherit = false) where T : Attribute; static IEnumerable WithAttribute(this IEnumerable types, Type attributeType, bool inherit = false); static IEnumerable> WithAttributeExpanded( this IEnumerable types, bool inherit = false) where T : Attribute; static IEnumerable> WithAttributeExpanded( this IEnumerable types, Type attributeType, bool inherit = false); static IEnumerable WhichIsGenericTypeOf(this IEnumerable types, Type of); static IEnumerable WhichIsGenericTypeOf(this IEnumerable types); static IEnumerable WhichInheritFromClass(this IEnumerable types, Type from); static IEnumerable WhichInheritFromClass(this IEnumerable types); } ``` ## Extension Methods — PropertyInfo ```csharp public static class PropertyInfoExtensionMethods { static bool IsIndexerProperty(this PropertyInfo prop); static bool IsPublic(this PropertyInfo prop); static IEnumerable WhichIsIndexerProperty(this IEnumerable props); } ``` ## Extension Methods — ParameterInfo ```csharp public static class ParameterInfoExtensions { static bool HasAttribute(this ParameterInfo param, Type attributeType, bool inherit = false); static bool HasAttribute(this ParameterInfo param, string attributeName, bool inherit = false); } public static class ParameterInfoEnumerableExtensions { static IEnumerable WithName(this IEnumerable params, string name, bool ignoreCase); static IEnumerable WithTypeOf(this IEnumerable params, Type type); static IEnumerable WithTypeOf(this IEnumerable params); static IEnumerable WithoutTypeOf(this IEnumerable params, Type type); static IEnumerable WithoutTypeOf(this IEnumerable params); static IEnumerable WithAttribute(this IEnumerable params, string attributeName); static IEnumerable WithoutAttribute(this IEnumerable params, string attributeName); } ``` ## Extension Methods — String ```csharp public static class StringExtensions { // Validation static bool IsNullOrWhiteSpace(this string? value); static bool IsNumeric(this string? value); static bool IsInt(this string? value); static bool IsLong(this string? value); static bool IsDouble(this string? value); static bool IsDateTime(this string? value); static bool IsBoolean(this string? value); static bool IsValidIp(this string? value); static bool IsBase64Encoded(this string? value); static bool IsLowerCase(this string? value); static bool IsUpperCase(this string? value); static bool IsValidEmailAddress(this string? value); static bool IsGuid(this string? value); // Conversion static int ToInt(this string value); static long ToLong(this string value); static double ToDouble(this string value); static float ToFloat(this string value); static decimal ToDecimal(this string value); static bool ToBoolean(this string value); static DateTime ToDateTime(this string value); static Guid ToGuid(this string value); static int? ToNullableInt(this string? value); static long? ToNullableLong(this string? value); static double? ToNullableDouble(this string? value); static float? ToNullableFloat(this string? value); static decimal? ToNullableDecimal(this string? value); static DateTime? ToNullableDateTime(this string? value); // Manipulation static string? TrimToNull(this string? value); static string? ToNull(this string? value); static string RemoveEmptyLines(this string value); static string RemoveEnd(this string value, string end); // Encoding static string EncodeToBase64(this string value); static string DecodeFromBase64(this string value); static string EncodeToBase58(this string value); static string DecodeFromBase58(this string value); // Pattern matching static bool Match(this string value, string matchString, bool ignoreCase = true, bool invert = false); } ``` ## Extension Methods — Enum ```csharp public static class EnumExtensions { static string GetName(this Enum value); static bool TryFind(Type enumType, string value, out object? result); static bool TryFind(Type enumType, string value, bool ignoreCase, out object? result); } ``` ## Extension Methods — DateTime ```csharp public static class DateTimeExtensions { static long ToUnixTimeMilliseconds(this DateTime dateTime); static DateTime FromUnixTimeMilliseconds(this long milliseconds); static DateTime FromUnixTimeSeconds(this long seconds); static DateTime ToUtc(this DateTime dateTime); static DateTime ToLocal(this DateTime dateTime); static DateTime SetKind(this DateTime dateTime, DateTimeKind kind); static DateTime SetIsUtc(this DateTime dateTime); static DateTime SetIsLocal(this DateTime dateTime); } ``` ## Extension Methods — Collections ```csharp // Merge (in Cocoar.Reflectensions.ExtensionMethods namespace) public static class IDictionaryExtensions { static T Merge(this T dict, T mergeWith) where T : IDictionary; } // Typed value access (in Cocoar.Reflectensions namespace) public static class IDictionaryExtensions { static T? GetValueAs(this IDictionary dictionary, string key, T? orDefault = default); static bool TryGetValueAs(this IDictionary dictionary, string key, out T? value); static T? GetValueTo(this IDictionary dictionary, string key, T? orDefault = default); static bool TryGetValueTo(this IDictionary dictionary, string key, out T? value); } public static class ArrayExtensions { static T[] Concat(this T[] first, T[] second); static T[] SubArray(this T[] source, int startIndex, int length); static T[] SubArray(this T[] source, int startIndex); } ``` ## Extension Methods — Assembly ```csharp public static class AssemblyExtensions { static Stream? ReadResourceAsStream(this Assembly assembly, string resourceName); static string? ReadResourceAsString(this Assembly assembly, string resourceName); static byte[]? ReadResourceAsByteArray(this Assembly assembly, string resourceName); } ``` ## Extension Methods — Task ```csharp public static class TaskExtensions { static Task CastToTaskOf(this Task task); } ``` ## Extension Methods — Claims ```csharp public static class ClaimExtensions { static IEnumerable GetClaimsByType(this IEnumerable claims, string type); static Claim? GetFirstClaimByType(this IEnumerable claims, string type); static string? GetFirstClaimValueByType(this IEnumerable claims, string type); static IEnumerable GetClaimValuesByType(this IEnumerable claims, string type); static IEnumerable RemoveClaimsByType(this IEnumerable claims, string type); } ``` ## Extension Methods — Action ```csharp public static class ActionExtensions { static T InvokeAction(this Action action, T? instance = default); static (T1, T2) InvokeAction(this Action action, T1? firstInstance = default, T2? secondInstance = default); static (T1, T2, T3) InvokeAction(this Action action, T1? firstInstance = default, T2? secondInstance = default, T3? thirdInstance = default); } ``` ## Helpers ```csharp public static class ArrayHelpers { static T[] ConcatArrays(params T[][] arrays); static T[] ConcatArrays(T[] first, T[] second); static T[] SubArray(T[] source, int startIndex, int length); } public static class Base58Helper { static string Encode(byte[] data); static byte[] Decode(string encoded); static string EncodeWithCheckSum(byte[] data); static byte[] DecodeWithCheckSum(string encoded); } public static class WildcardHelper { static string WildcardToRegex(string pattern); static bool Match(string input, string pattern, bool caseSensitive = true); static bool ContainsWildcard(string pattern); } public class Enum where T : struct, Enum, IConvertible { static T Find(string value, T ifNotFound = default); static T Find(string value, bool ignoreCase, T ifNotFound = default); static bool TryFind(string value, out T result); static bool TryFind(string value, bool ignoreCase, out T result); } ``` ## InvokeHelper (Invoke package) ```csharp public static class InvokeHelper { // Synchronous static void InvokeVoidMethod(object? instance, MethodInfo method, params object[] parameters); static T? InvokeMethod(object? instance, MethodInfo method, params object[] parameters); static object InvokeMethod(object? instance, MethodInfo method, params object[] parameters); // Asynchronous static Task InvokeVoidMethodAsync(object? instance, MethodInfo method, params object[] parameters); static Task InvokeMethodAsync(object? instance, MethodInfo method, params object[] parameters); static Task InvokeMethodAsync(object? instance, MethodInfo method, params object[] parameters); // Generic methods (MethodInfo + type arguments) static void InvokeGenericVoidMethod(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static void InvokeGenericVoidMethod(object instance, MethodInfo method, params object[] parameters); static void InvokeGenericVoidMethod(object instance, MethodInfo method, params object[] parameters); static void InvokeGenericVoidMethod(object instance, MethodInfo method, params object[] parameters); static object? InvokeGenericMethod(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static TResult? InvokeGenericMethod(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static TResult? InvokeGenericMethod(object instance, MethodInfo method, params object[] parameters); static TResult? InvokeGenericMethod(object instance, MethodInfo method, params object[] parameters); static TResult? InvokeGenericMethod(object instance, MethodInfo method, params object[] parameters); // Async generic methods static Task InvokeGenericVoidMethodAsync(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static Task InvokeGenericMethodAsync(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static Task InvokeGenericMethodAsync(object instance, MethodInfo method, IEnumerable genericArguments, params object[] parameters); static Task InvokeGenericMethodAsync(object instance, MethodInfo method, params object[] parameters); static Task InvokeGenericMethodAsync(object instance, MethodInfo method, params object[] parameters); static Task InvokeGenericMethodAsync(object instance, MethodInfo method, params object[] parameters); } ``` ## ExpandableObject (ExpandableObject package) ```csharp public class ExpandableObject : ExpandableBaseObject { ExpandableObject(); ExpandableObject(object? source); ExpandableObject(IDictionary dictionary); ExpandableObject(IDictionary dictionary); static implicit operator ExpandableObject(Dictionary dictionary); } public abstract class ExpandableBaseObject : DynamicObject, INotifyPropertyChanged { T? GetValue(string key); object? GetValue(string key); T? GetValueOrDefault(string key, T? defaultValue = default); object? GetValueOrDefault(string key, object? defaultValue = default); IEnumerable GetKeys(); IEnumerable GetValues(); IEnumerable> GetProperties(); bool ContainsKey(string key); bool IsInstanceProperty(string key); event PropertyChangedEventHandler? PropertyChanged; } ``` --- --- url: /guide/dynamic-invocation.md --- # Dynamic Invocation The `Cocoar.Reflectensions.Invoke` package provides helpers for invoking methods dynamically via `MethodInfo` — with automatic parameter type conversion and async support. ## Installation ```bash dotnet add package Cocoar.Reflectensions.Invoke ``` ## InvokeHelper All InvokeHelper methods accept a `MethodInfo` to identify the target method. ### Synchronous Invocation #### InvokeMethod\ Invoke a method and return a typed result: ```csharp using Cocoar.Reflectensions.Helper; var calculator = new Calculator(); MethodInfo method = typeof(Calculator).GetMethod("Add")!; int result = InvokeHelper.InvokeMethod(calculator, method, 10, 20); ``` #### InvokeVoidMethod Invoke a method with no return value: ```csharp MethodInfo method = typeof(MyService).GetMethod("Initialize")!; InvokeHelper.InvokeVoidMethod(myService, method, config); ``` ::: info If the method returns `Task` or `Task`, the void variants synchronously wait for completion. ::: ### Asynchronous Invocation #### InvokeMethodAsync\ Invoke an async method and await the result: ```csharp MethodInfo method = typeof(MyService).GetMethod("FetchDataAsync")!; var result = await InvokeHelper.InvokeMethodAsync( myService, method, "https://api.example.com"); ``` #### InvokeVoidMethodAsync Invoke an async void method: ```csharp MethodInfo method = typeof(MyService).GetMethod("ProcessAsync")!; await InvokeHelper.InvokeVoidMethodAsync(myService, method, data); ``` ### Generic Method Invocation Invoke generic methods by passing `MethodInfo` and generic type arguments: ```csharp MethodInfo method = typeof(MyService).GetMethod("Serialize")!; // Invoke with explicit generic arguments var result = InvokeHelper.InvokeGenericMethod( myService, method, new[] { typeof(MyClass) }, // generic type arguments myObject); // Void generic method MethodInfo registerMethod = typeof(MyService).GetMethod("Register")!; InvokeHelper.InvokeGenericVoidMethod( myService, registerMethod, new[] { typeof(IMyService), typeof(MyServiceImpl) }); // Async generic method MethodInfo deserializeMethod = typeof(MyService).GetMethod("DeserializeAsync")!; var result2 = await InvokeHelper.InvokeGenericMethodAsync( myService, deserializeMethod, new[] { typeof(MyClass) }, jsonString); ``` There are also convenience overloads that accept generic type arguments as type parameters instead of an array: ```csharp // Type args as generic parameters (1-3 args supported) var result = InvokeHelper.InvokeGenericMethod( myService, method, myObject); // Equivalent to: InvokeGenericMethod(instance, method, [typeof(MyClass)], ...) ``` ## Automatic Parameter Conversion InvokeHelper automatically converts parameters to match the method signature using the same smart conversion pipeline as `Reflect().To()`: ```csharp public class Calculator { public int Add(int a, int b) => a + b; } MethodInfo method = typeof(Calculator).GetMethod("Add")!; // String arguments are auto-converted to int var result = InvokeHelper.InvokeMethod( new Calculator(), method, "10", "20"); // Returns: 30 ``` ## Static Method Invocation Pass `null` as the instance to invoke static methods: ```csharp MethodInfo method = typeof(Math).GetMethod("Max", new[] { typeof(int), typeof(int) })!; var result = InvokeHelper.InvokeMethod(null, method, 10, 20); // Returns: 20 ``` --- --- url: /guide/expandable-objects.md --- # Expandable Objects The `Cocoar.Reflectensions.ExpandableObject` package provides dynamic objects with dictionary-like behavior and property change notifications. ## Installation ```bash dotnet add package Cocoar.Reflectensions.ExpandableObject ``` ## Creating Expandable Objects ### Empty Object ```csharp using Cocoar.Reflectensions; var obj = new ExpandableObject(); ``` ### From an Existing Object Copies all public properties: ```csharp var person = new Person { Name = "Alice", Age = 30 }; var obj = new ExpandableObject(person); ``` ### From a Dictionary ```csharp var dict = new Dictionary { ["Name"] = "Alice", ["Age"] = 30 }; var obj = new ExpandableObject(dict); ``` ### Implicit Conversion from Dictionary ```csharp ExpandableObject obj = new Dictionary { ["Name"] = "Alice", ["Age"] = 30 }; ``` ## Dynamic Property Access ExpandableObject inherits from `DynamicObject`, so you can use `dynamic` to access properties: ```csharp dynamic obj = new ExpandableObject(); obj.Name = "Alice"; obj.Age = 30; Console.WriteLine(obj.Name); // "Alice" Console.WriteLine(obj.Age); // 30 ``` ## Dictionary-Like Operations ### Reading Values ```csharp var obj = new ExpandableObject(new { Name = "Alice", Age = 30 }); // Get value with type conversion (throws KeyNotFoundException if missing) string name = obj.GetValue("Name"); int age = obj.GetValue("Age"); // Get with default (returns default if missing) string email = obj.GetValueOrDefault("Email", "none"); ``` ### Writing Values Use `dynamic` access to set values: ```csharp dynamic d = obj; d.Name = "Bob"; d.NewProperty = "dynamic value"; ``` ### Querying ```csharp // Check if property exists bool has = obj.ContainsKey("Name"); // true // Get all keys IEnumerable keys = obj.GetKeys(); // Get all values IEnumerable values = obj.GetValues(); // Get all properties as key-value pairs var properties = obj.GetProperties(); ``` ### Instance vs Dynamic Properties Check whether a property comes from the original object type or was added dynamically: ```csharp var person = new Person { Name = "Alice" }; var obj = new ExpandableObject(person); dynamic d = obj; d.DynamicProp = "value"; bool isInstance = obj.IsInstanceProperty("Name"); // true bool isDynamic = obj.IsInstanceProperty("DynamicProp"); // false ``` ## Property Change Notifications ExpandableObject implements `INotifyPropertyChanged`: ```csharp var obj = new ExpandableObject(); obj.PropertyChanged += (sender, e) => { Console.WriteLine($"Property changed: {e.PropertyName}"); }; dynamic d = obj; d.Name = "Alice"; // Prints: "Property changed: Name" ``` ## IDictionary Support ExpandableObject supports `IDictionary` for interoperability: ```csharp var dict = new Hashtable { ["key"] = "value" }; var obj = new ExpandableObject(dict); ``` --- --- url: /guide/getting-started.md --- # Getting Started ## Installation Install the core package via NuGet: ```bash dotnet add package Cocoar.Reflectensions ``` For dynamic method invocation or expandable objects, add the specialized packages: ```bash dotnet add package Cocoar.Reflectensions.Invoke dotnet add package Cocoar.Reflectensions.ExpandableObject ``` ::: tip The core package has **zero external dependencies** and targets both .NET 8.0 and .NET Standard 2.0. ::: ## Quick Examples ### 1. Parse Type Names from Strings Resolve complex generic types from human-readable strings: ```csharp using Cocoar.Reflectensions; // Parse generic types from strings var type = TypeHelper.FindType("Dictionary>"); // Returns: typeof(Dictionary>) // Works with CLR syntax too var type2 = TypeHelper.FindType( "System.Collections.Generic.Dictionary`2[System.String, System.Int32]"); // Custom type mappings (e.g. TypeScript to C#) var mapping = new Dictionary { ["number"] = "double" }; var type3 = TypeHelper.FindType("List", mapping); // Returns: typeof(List) ``` ### 2. Query Reflection with Fluent Extensions Filter methods, check type relationships, and inspect types using chainable extension methods: ```csharp using Cocoar.Reflectensions; // Filter methods fluently var methods = typeof(Calculator).GetMethods() .WithName("Calculate") .WithReturnType() .WithParametersOfType(typeof(int), typeof(int)); // Check type relationships bool isDict = type.IsGenericTypeOf>(); bool inherits = type.InheritFromClass(); bool implements = type.ImplementsInterface(); bool castable = type.IsImplicitCastableTo(); ``` ### 3. Convert Objects with Smart Type Conversion Convert between types intelligently using the `Reflect()` entry point: ```csharp using Cocoar.Reflectensions; // Parse a date string string dateStr = "2021-03-21T15:50:17+00:00"; DateTime date = dateStr.Reflect().To(); // With fallback value object maybeNumber = "not a number"; int number = maybeNumber.Reflect().To(42); // Returns 42 // Try pattern for safe conversion if (value.Reflect().TryTo(out var result)) { Console.WriteLine($"Parsed: {result}"); } ``` ## Next Steps * [Why Reflectensions?](/guide/why-cocoar-reflectensions) — what problems this library solves * [Type Parsing](/guide/type-parsing) — full type resolution reference * [Type Extensions](/guide/type-extensions) — all type checking methods * [Smart Type Conversion](/guide/type-conversion) — conversion strategies in detail * [API Overview](/reference/api) — complete API reference --- --- url: /guide/method-extensions.md --- # Method Extensions Reflectensions provides fluent, LINQ-style filtering for `MethodInfo`, `PropertyInfo`, and `ParameterInfo` collections. ## Filtering Methods ### WithName Filter methods by name: ```csharp var methods = typeof(MyClass).GetMethods() .WithName("Calculate"); // Case-insensitive var methods2 = typeof(MyClass).GetMethods() .WithName("calculate", StringComparison.OrdinalIgnoreCase); ``` ### WithReturnType Filter by return type: ```csharp var methods = typeof(MyClass).GetMethods() .WithReturnType(); // Non-generic overload var methods2 = typeof(MyClass).GetMethods() .WithReturnType(typeof(Task)); ``` ### WithParametersOfType Filter by exact parameter types: ```csharp var methods = typeof(MyClass).GetMethods() .WithParametersOfType(typeof(string), typeof(int)); ``` ### WithParametersLengthOf Filter by parameter count: ```csharp var methods = typeof(MyClass).GetMethods() .WithParametersLengthOf(2); // Include optional parameters in count var methods2 = typeof(MyClass).GetMethods() .WithParametersLengthOf(3, includeOptional: true); ``` ### WithAttribute Filter methods decorated with a specific attribute: ```csharp var methods = typeof(MyClass).GetMethods() .WithAttribute(); // Include inherited attributes var methods2 = typeof(MyClass).GetMethods() .WithAttribute(inherit: true); ``` ### WithAttributeExpanded Filter and return both the method and the attribute instance: ```csharp var methods = typeof(MyClass).GetMethods() .WithAttributeExpanded(); foreach (var (method, attr) in methods) { Console.WriteLine($"{method.Name}: {attr.Message}"); } ``` ### WithGenericArgumentsLengthOf / WithGenericArgumentsOfType Filter generic methods: ```csharp // Methods with exactly 1 generic argument var methods = typeof(MyClass).GetMethods() .WithGenericArgumentsLengthOf(1); // Methods with specific generic argument types var methods2 = typeof(MyClass).GetMethods() .WithGenericArgumentsOfType(typeof(string)); ``` ## Chaining Filters All filters are composable — chain them for precise queries: ```csharp var handlers = typeof(MyController).GetMethods() .WithName("Handle") .WithReturnType() .WithParametersOfType(typeof(HttpRequest)) .WithAttribute(); ``` ## Single Method Checks Each filter has a corresponding single-method check: ```csharp MethodInfo method = ...; bool nameMatch = method.HasName("Calculate"); bool paramMatch = method.HasParametersLengthOf(2); bool typeMatch = method.HasParametersOfType(new[] { typeof(int), typeof(int) }); bool attrMatch = method.HasAttribute(); bool retMatch = method.HasReturnType(); ``` ## Extension Method Detection Check if a method is an extension method: ```csharp MethodInfo method = ...; bool isExtension = method.IsExtensionMethod(); // true if declared in static class with 'this' first parameter ``` ## Property Extensions ### IsIndexerProperty ```csharp PropertyInfo prop = ...; bool isIndexer = prop.IsIndexerProperty(); ``` ### IsPublic ```csharp bool isPublic = prop.IsPublic(); ``` ### Filtering Properties ```csharp var indexers = typeof(MyClass).GetProperties() .WhichIsIndexerProperty(); ``` ## Parameter Extensions ### Filtering Parameters ```csharp MethodInfo method = ...; var parameters = method.GetParameters(); // Filter by name (ignoreCase is required) var named = parameters.WithName("id", ignoreCase: true); // Filter by type var strings = parameters.WithTypeOf(); // Exclude types var nonStrings = parameters.WithoutTypeOf(); // Filter by attribute name (string-based matching) var validated = parameters.WithAttribute("Required"); var notValidated = parameters.WithoutAttribute("Required"); ``` ### Checking Parameters ```csharp ParameterInfo param = ...; // Check by attribute type bool hasAttr = param.HasAttribute(typeof(RequiredAttribute)); // Check by attribute name (string matching) bool hasAttrByName = param.HasAttribute("Required"); ``` --- --- url: /guide/migration.md --- # Migration from doob.Reflectensions Cocoar.Reflectensions v1.0.0 is the successor to `doob.Reflectensions` v6.4.2, with a new organization, simplified package structure, and zero external dependencies. ## Package References ```xml ``` ## Namespace Updates ```csharp // Before using doob.Reflectensions; using doob.Reflectensions.ExtensionMethods; using doob.Reflectensions.Json; // After using Cocoar.Reflectensions; using Cocoar.Reflectensions.ExtensionMethods; // Json namespace removed — use System.Text.Json ``` ## Package Changes | Old Package | Action | New Package | |---|---|---| | `doob.Reflectensions` | Renamed | `Cocoar.Reflectensions` | | `doob.Reflectensions.Invoke` | Renamed | `Cocoar.Reflectensions.Invoke` | | `doob.Reflectensions.ExpandableObject` | Renamed | `Cocoar.Reflectensions.ExpandableObject` | | `doob.Reflectensions.CommonExtensions` | **Merged** into core | `Cocoar.Reflectensions` | | `doob.Reflectensions.Json` | **Removed** | Use `System.Text.Json` directly | | `doob.Reflectensions.CodeDefinition` | **Removed** | — | | `doob.Reflectensions.AspNetCore` | **Removed** | — | ## JSON Migration The JSON wrapper has been removed. Replace with `System.Text.Json`: ```csharp // Before (doob.Reflectensions.Json) using doob.Reflectensions.Json; var json = Json.Converter.ToJson(obj); var obj = Json.Converter.ToObject(json); // After (System.Text.Json) using System.Text.Json; var json = JsonSerializer.Serialize(obj); var obj = JsonSerializer.Deserialize(json); ``` ## Type Name Strings If you have hardcoded type name strings referencing the old namespace, update them: ```csharp // Before TypeHelper.FindType("doob.Reflectensions.Tests.MyClass") // After TypeHelper.FindType("Cocoar.Reflectensions.Tests.MyClass") ``` ## Target Framework Changes | Before | After | |---|---| | .NET 7.0 | .NET 8.0 | | .NET Framework 4.7.2 | — (removed) | | .NET Standard 2.0 | .NET Standard 2.0 (kept) | If you were targeting .NET Framework 4.7.2 directly, you can still use Reflectensions through .NET Standard 2.0 compatibility (.NET Framework 4.6.1+). ## What's New in v1.0.0 * **Zero external dependencies** — Newtonsoft.Json dependency removed * **Nullable reference types** enabled across all projects * **Performance optimizations** — hot paths for common conversions (8x faster for int to string) * **529 passing tests** with CI/CD validation * **Full XML documentation** for IntelliSense --- --- url: /reference/packages.md --- # Packages Cocoar.Reflectensions is split into focused packages. Install only what you need. ## Core {#core} ### Cocoar.Reflectensions The main library with all core functionality. ```bash dotnet add package Cocoar.Reflectensions ``` | | | |---|---| | **NuGet** | [Cocoar.Reflectensions](https://www.nuget.org/packages/Cocoar.Reflectensions) | | **Targets** | .NET 8.0, .NET Standard 2.0 | | **Dependencies** | None | **Includes:** * `TypeHelper` — type parsing and resolution from strings * Type extensions — `IsNumericType()`, `IsGenericTypeOf()`, `ImplementsInterface()`, etc. * Object reflection — `Reflect()`, `To()`, `TryTo()`, `As()`, property access * Method/property/parameter extensions — fluent LINQ-style filtering * String extensions — validation, conversion, encoding * Enum extensions — `GetName()`, `TryFind()` * DateTime extensions — Unix time, UTC conversion * Array/dictionary extensions — `Concat()`, `SubArray()`, `Merge()` * Assembly extensions — embedded resource reading * Helpers — `Base58Helper`, `WildcardHelper`, `Enum`, `ArrayHelpers` ## Invoke {#invoke} ### Cocoar.Reflectensions.Invoke Dynamic method invocation with automatic parameter matching. ```bash dotnet add package Cocoar.Reflectensions.Invoke ``` | | | |---|---| | **NuGet** | [Cocoar.Reflectensions.Invoke](https://www.nuget.org/packages/Cocoar.Reflectensions.Invoke) | | **Targets** | .NET 8.0, .NET Standard 2.0 | | **Dependencies** | Cocoar.Reflectensions | **Includes:** * `InvokeHelper` — invoke methods via `MethodInfo` * Synchronous and asynchronous invocation * Generic method invocation with type arguments * Automatic parameter type conversion ## Expandable Object {#expandable-object} ### Cocoar.Reflectensions.ExpandableObject Dynamic objects with dictionary-like behavior. ```bash dotnet add package Cocoar.Reflectensions.ExpandableObject ``` | | | |---|---| | **NuGet** | [Cocoar.Reflectensions.ExpandableObject](https://www.nuget.org/packages/Cocoar.Reflectensions.ExpandableObject) | | **Targets** | .NET 8.0, .NET Standard 2.0 | | **Dependencies** | Cocoar.Reflectensions | **Includes:** * `ExpandableObject` — dynamic object wrapper * Initialize from objects, dictionaries, or empty * `INotifyPropertyChanged` support * Implicit conversion from `Dictionary` ## Which Package Do I Need? | Use case | Package | |----------|---------| | Type parsing, type checking, type conversion | Core | | Fluent method/property filtering | Core | | String validation and conversion | Core | | Dynamic method invocation via MethodInfo | Core + Invoke | | Dynamic objects with dictionary behavior | Core + ExpandableObject | | Everything | All three | ## Compatibility All packages target: * **.NET 8.0** — recommended for new projects * **.NET Standard 2.0** — compatible with: * .NET Core 2.0+ * .NET 5+ * .NET Framework 4.6.1+ * Mono 5.4+ * Xamarin.iOS 10.14+ * Xamarin.Android 8.0+ --- --- url: /guide/object-reflection.md --- # Reflection Wrapper Reflectensions avoids polluting `object` with extension methods. Instead, all object-level operations go through a lightweight wrapper accessed via `.Reflect()`. ## The Reflect() Entry Point Call `.Reflect()` on any object to access reflection extensions: ```csharp using Cocoar.Reflectensions; var value = "hello"; var reflected = value.Reflect(); // Now use reflection extensions DateTime date = "2021-03-21".Reflect().To(); int number = 42.Reflect().To().Length; ``` There is also a generic version that preserves type information: ```csharp // Generic — avoids boxing for value types int n = 42; var reflected = n.Reflect(); // ObjectReflection ``` ## Zero-Cost Abstraction Both `ObjectReflection` and `ObjectReflection` are **readonly structs**. They live on the stack with no heap allocations: ```csharp // No heap allocation — ObjectReflection is a struct var reflected = 42.Reflect(); string s = reflected.To(); // "42" ``` The generic variant `ObjectReflection` avoids boxing entirely for value types. When you call `.Reflect()` on an `int`, the value stays unboxed throughout the conversion pipeline. ## ObjectReflection vs ObjectReflection\ | | `ObjectReflection` | `ObjectReflection` | |---|---|---| | Created by | `.Reflect()` on `object` | `.Reflect()` on typed values | | Boxing | Value is boxed | No boxing for value types | | Use case | Runtime-typed values | Compile-time typed values | | Both are | readonly struct | readonly struct | `ObjectReflection` implicitly converts to `ObjectReflection`, so all extension methods work with both: ```csharp // Generic wrapper — no boxing ObjectReflection generic = 42.Reflect(); // Implicit conversion to non-generic ObjectReflection nonGeneric = generic; ``` ## Property Access Read and write properties by name: ```csharp var person = new Person { Name = "Alice", Age = 30 }; // Get property value string name = person.Reflect().GetPropertyValue("Name"); // Set property value person.Reflect().SetPropertyValue("Age", 31); ``` `GetPropertyValue` supports custom `BindingFlags`: ```csharp var value = obj.Reflect().GetPropertyValue( "InternalProp", BindingFlags.NonPublic | BindingFlags.Instance); ``` `SetPropertyValue` supports nested paths — it navigates through object properties to set deeply nested values. ## Equality Helpers ### EqualsToAnyOf Check if a value equals any of the provided values: ```csharp string status = "active"; bool match = status.Reflect().EqualsToAnyOf("active", "enabled", "on"); // true ``` ### ToBoolean Convert a value to `bool` using custom true-value definitions: ```csharp object value = "yes"; bool result = value.Reflect().ToBoolean("yes", "true", "1", "on"); // true ``` ## Implicit Cast Checking Check if a value's runtime type can be implicitly cast to a target type: ```csharp object value = 42; bool canCast = value.Reflect().IsImplicitCastableTo(); // true bool canCast2 = value.Reflect().IsImplicitCastableTo(); // false ``` --- --- url: /guide/type-conversion.md --- # Smart Type Conversion Reflectensions provides intelligent type conversion through the `Reflect()` entry point. It automatically tries multiple conversion strategies in order, with optimized fast paths for common type pairs. ## Basic Conversion ### To\() Converts a value to the target type, throwing if conversion fails: ```csharp string dateStr = "2021-03-21T15:50:17+00:00"; DateTime date = dateStr.Reflect().To(); string numberStr = "42"; int number = numberStr.Reflect().To(); int n = 123; string s = n.Reflect().To(); // "123" ``` ### To\(T defaultValue) Returns the default value if conversion fails: ```csharp object value = "not a number"; int number = value.Reflect().To(42); // 42 object maybeDate = "invalid"; DateTime date = maybeDate.Reflect().To(DateTime.MinValue); ``` ### TryTo\() The Try-pattern for safe conversion without exceptions: ```csharp if (value.Reflect().TryTo(out var result)) { Console.WriteLine($"Parsed: {result}"); } else { Console.WriteLine("Conversion failed"); } ``` ## Casting vs Converting Reflectensions distinguishes between **casting** (`As`) and **converting** (`To`): ### As\() — Type Switching `As` attempts to switch the type without data transformation. It tries direct casting and reference conversion: ```csharp object obj = "hello"; string? s = obj.Reflect().As(); // "hello" — direct cast object num = 42; long? l = num.Reflect().As(); // null — int is not long ``` ### TryAs\() ```csharp if (obj.Reflect().TryAs(out var str)) { // Direct cast succeeded } ``` ### To\() — Smart Conversion `To` uses a multi-strategy conversion pipeline. In order, it tries: 1. **Bool special case** — if the target is `bool`, uses the `ToBoolean()` logic directly 2. **Direct cast** via `As` (type identity, inheritance, interface) 3. **Nullable unwrapping** — if the target is `Nullable`, converts to the underlying type 4. **Guid special case** — `Guid.TryParse` for string-to-Guid conversion 5. **Numeric conversion** — `Convert.ChangeType` when the target is a numeric type 6. **IConvertible** — `Convert.ChangeType` when both source and target implement `IConvertible` 7. **Implicit operator detection** — finds and invokes `op_Implicit` on source or target type ```csharp // Uses int.Parse() internally int n = "42".Reflect().To(); // Uses Guid.TryParse() internally Guid id = "550e8400-e29b-41d4-a716-446655440000".Reflect().To(); // Uses ToString() internally string s = 42.Reflect().To(); ``` ## Automatic Implicit Operator Conversion This is where Reflectensions really shines. When you define `implicit operator` on your types, `To()` **discovers and uses them automatically** — no extra code needed. ### How It Works When all other conversion strategies fail, `To()` searches for an `op_Implicit` method on **both** the source type and the target type. This means it works regardless of which side defines the operator: ```csharp public class Celsius { public double Value { get; } public Celsius(double value) => Value = value; // Define implicit conversion TO Fahrenheit public static implicit operator Fahrenheit(Celsius c) => new(c.Value * 9.0 / 5.0 + 32); } public class Fahrenheit { public double Value { get; } public Fahrenheit(double value) => Value = value; } // Reflectensions finds and uses the implicit operator automatically var celsius = new Celsius(100); Fahrenheit f = celsius.Reflect().To(); // f.Value == 212 ``` ### Bidirectional Discovery The key insight: `GetImplicitCastMethodTo` searches on **both** the source and target types. If the operator is defined on the target type (accepting the source type as parameter), it works just as well: ```csharp public class Camaro { } public class Truck : Car { // Implicit conversion defined on the TARGET type public static implicit operator Truck(Camaro camaro) => new Truck(); } // Works — found on Truck, even though we start from Camaro var camaro = new Camaro(); Truck truck = camaro.Reflect().To(); ``` ### Checking Castability Before Converting You can check whether an implicit conversion exists without performing it: ```csharp // On types bool canConvert = typeof(Camaro).IsImplicitCastableTo(); // true bool canReverse = typeof(Truck).IsImplicitCastableTo(); // false // On values bool canConvert2 = camaro.Reflect().IsImplicitCastableTo(); // true ``` ### Discovering Operator Methods Inspect all implicit or explicit operators defined on a type: ```csharp // List all implicit operators var implicits = typeof(Truck).GetImplicitOperatorMethods(); foreach (var method in implicits) { var param = method.GetParameters()[0].ParameterType; Console.WriteLine($"{param.Name} -> {method.ReturnType.Name}"); } // Output: "Camaro -> Truck" // List all explicit operators var explicits = typeof(MyType).GetExplicitOperatorMethods(); // Find a specific conversion MethodInfo? cast = typeof(Camaro).GetImplicitCastMethodTo(); MethodInfo? explicit_ = typeof(MyType).GetExplicitCastMethodTo(); ``` ### Numeric Widening For numeric types, Reflectensions maintains a built-in [implicit conversion table](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/numeric-conversions) that mirrors C# language rules. This means `IsImplicitCastableTo` works correctly for all numeric widening conversions without needing to search for `op_Implicit`: ```csharp typeof(int).IsImplicitCastableTo(); // true (widening) typeof(int).IsImplicitCastableTo(); // true (widening) typeof(int).IsImplicitCastableTo(); // true (widening) typeof(long).IsImplicitCastableTo(); // false (narrowing) typeof(float).IsImplicitCastableTo(); // true typeof(double).IsImplicitCastableTo(); // false typeof(byte).IsImplicitCastableTo(); // true typeof(char).IsImplicitCastableTo(); // true ``` ::: info The conversion pipeline order matters. Direct casts and `IConvertible` are tried before implicit operators, so operator conversion only kicks in when standard .NET mechanisms don't apply — exactly the right fallback for your custom domain types. ::: ## Non-Generic Overloads All conversion methods have non-generic overloads that accept a `Type` parameter: ```csharp Type targetType = typeof(DateTime); object? result = value.Reflect().To(targetType); object? result2 = value.Reflect().To(targetType, defaultValue); bool success = value.Reflect().TryTo(targetType, out object? result3); ``` ## Performance: Hot Paths Common conversions are optimized with dedicated fast paths that bypass the generic pipeline: | Conversion | Performance | Mechanism | |-----------|-------------|-----------| | `int` to `string` | ~80ns | Direct `ToString()` | | `string` to `int` | ~118ns | Direct `int.Parse()` | | `long` to `string` | ~80ns | Direct `ToString()` | | `string` to `long` | ~118ns | Direct `long.Parse()` | | `double` to `string` | ~80ns | Direct `ToString()` | | `string` to `double` | ~118ns | Direct `double.Parse()` | | `bool` to `string` | ~80ns | Direct `ToString()` | These fast paths are selected automatically based on the source and target types. All other conversions fall back to the generic pipeline. ::: tip The `int.Reflect().To()` hot path is **8x faster** than the generic fallback path. ::: ## Real-World Patterns ### Configuration Parsing ```csharp public T GetConfigValue(Dictionary config, string key, T fallback) { if (!config.TryGetValue(key, out var value)) return fallback; return value.Reflect().TryTo(out var result) ? result : fallback; } ``` ### Loose Data Normalization ```csharp // Convert loosely-typed API responses var response = new Dictionary { ["count"] = "42", ["enabled"] = "true", ["timestamp"] = "2021-03-21T15:50:17Z" }; int count = response["count"].Reflect().To(); bool enabled = response["enabled"].Reflect().To(); DateTime ts = response["timestamp"].Reflect().To(); ``` --- --- url: /guide/string-extensions.md --- # String Extensions The core package includes a rich set of string extension methods for validation, conversion, and manipulation. ## Validation Check string content type without parsing: ```csharp "42".IsNumeric(); // true "42".IsInt(); // true "42".IsLong(); // true "3.14".IsDouble(); // true "true".IsBoolean(); // true "2021-03-21".IsDateTime();// true "192.168.1.1".IsValidIp(); // true "test@example.com".IsValidEmailAddress(); // true "SGVsbG8=".IsBase64Encoded(); // true "550e8400-e29b-41d4-a716-446655440000".IsGuid(); // true "hello".IsLowerCase(); // true "HELLO".IsUpperCase(); // true ``` ## Type Conversion Convert strings directly to typed values: ```csharp int n = "42".ToInt(); long l = "42".ToLong(); double d = "3.14".ToDouble(); float f = "3.14".ToFloat(); decimal dec = "3.14".ToDecimal(); bool b = "true".ToBoolean(); DateTime dt = "2021-03-21".ToDateTime(); Guid id = "550e8400-e29b-41d4-a716-446655440000".ToGuid(); ``` ### Nullable Variants Return `null` instead of throwing on invalid input: ```csharp int? n = "not a number".ToNullableInt(); // null long? l = "not a number".ToNullableLong(); // null double? d = "invalid".ToNullableDouble(); // null float? f = "invalid".ToNullableFloat(); // null decimal? dec = "invalid".ToNullableDecimal(); // null DateTime? dt = "invalid".ToNullableDateTime(); // null ``` ## String Manipulation ### Trimming ```csharp " hello ".Trim(); // "hello" (standard) " hello ".TrimToNull(); // "hello" " ".TrimToNull(); // null (whitespace-only becomes null) "".TrimToNull(); // null ``` ### Null / Empty Checks ```csharp string? s = null; s.IsNullOrEmpty(); // true s.IsNullOrWhiteSpace(); // true "".IsNullOrEmpty(); // true " ".IsNullOrWhiteSpace();// true ``` ### ToNull Converts empty or whitespace strings to `null`: ```csharp "".ToNull(); // null " ".ToNull(); // null "hello".ToNull(); // "hello" ``` ### RemoveEmptyLines Removes empty lines from multi-line strings: ```csharp "line1\n\nline2\n\nline3".RemoveEmptyLines(); // "line1\nline2\nline3" ``` ### RemoveEnd Removes a suffix from a string: ```csharp "HelloWorld".RemoveEnd("World"); // "Hello" ``` ## Wildcard Matching Match strings against wildcard patterns (`*`, `?`, `+`): ```csharp "hello world".Match("hello*"); // true (case-insensitive by default) "test.txt".Match("*.txt"); // true "abc".Match("a?c"); // true "Hello".Match("hello", ignoreCase: false); // false "test.txt".Match("*.txt", invert: true); // false (inverts result) ``` ## Encoding ### Base64 ```csharp string encoded = "Hello".EncodeToBase64(); // "SGVsbG8=" string decoded = "SGVsbG8=".DecodeFromBase64(); // "Hello" ``` ### Base58 ```csharp string encoded = "Hello".EncodeToBase58(); string decoded = encoded.DecodeFromBase58(); ``` ## Splitting Split with cleaner syntax: ```csharp "a,b,,c".Split(","); // ["a", "b", "", "c"] ``` --- --- url: /guide/type-extensions.md --- # Type Extensions Extension methods on `System.Type` for checking type characteristics, relationships, and operator availability. ## Type Checking ### IsNumericType Returns `true` for all numeric types (`int`, `long`, `float`, `double`, `decimal`, `byte`, `short`, `uint`, `ulong`, `ushort`, `sbyte`): ```csharp typeof(int).IsNumericType(); // true typeof(decimal).IsNumericType(); // true typeof(string).IsNumericType(); // false ``` ### IsNullableType Checks if the type is `Nullable`: ```csharp typeof(int?).IsNullableType(); // true typeof(int).IsNullableType(); // false typeof(string).IsNullableType(); // false ``` ### IsEnumerableType Checks if the type implements `IEnumerable` (excluding `string`): ```csharp typeof(List).IsEnumerableType(); // true typeof(int[]).IsEnumerableType(); // true typeof(string).IsEnumerableType(); // false ``` ### IsDictionaryType Checks if the type implements `IDictionary<,>`: ```csharp typeof(Dictionary).IsDictionaryType(); // true typeof(List).IsDictionaryType(); // false ``` ### IsStatic Checks if a type is static (abstract + sealed): ```csharp typeof(Math).IsStatic(); // true typeof(string).IsStatic(); // false ``` ## Type Relationships ### IsGenericTypeOf Checks if a type is a specific open generic type: ```csharp typeof(List).IsGenericTypeOf>(); // true typeof(Dictionary).IsGenericTypeOf>(); // true typeof(List).IsGenericTypeOf>(); // false ``` ### ImplementsInterface Checks if a type implements a given interface: ```csharp typeof(List).ImplementsInterface(); // true typeof(List).ImplementsInterface(); // false typeof(Dictionary).ImplementsInterface>(); // true ``` ### InheritFromClass Checks if a type inherits from a given class — by type, generic, or name: ```csharp typeof(MyDerived).InheritFromClass(); // true typeof(MyDerived).InheritFromClass(typeof(MyBase)); // true typeof(MyDerived).InheritFromClass("MyBase"); // true (name match) ``` ### InheritFromClassLevel Returns the inheritance depth between a type and an ancestor. Returns `0` if no relationship exists: ```csharp // class A { } // class B : A { } // class C : B { } typeof(C).InheritFromClassLevel(); // 2 typeof(B).InheritFromClassLevel(); // 1 typeof(A).InheritFromClassLevel(); // 0 (same type) ``` You can limit search depth with `maximumLevel`: ```csharp typeof(C).InheritFromClassLevel(maximumLevel: 1); // 0 (not found within 1 level) ``` ### IsImplicitCastableTo Checks if a type can be implicitly cast to another type (including numeric widening): ```csharp typeof(int).IsImplicitCastableTo(); // true typeof(int).IsImplicitCastableTo(); // true typeof(long).IsImplicitCastableTo(); // false ``` ## Attribute Checking ### HasAttribute Checks if a type is decorated with a specific attribute: ```csharp typeof(MyClass).HasAttribute(); // true/false typeof(MyClass).HasAttribute(inherit: true); // check base types too ``` ## Operator Methods ### GetImplicitOperatorMethods / GetExplicitOperatorMethods Retrieves all implicit or explicit conversion operator methods defined on a type: ```csharp var implicits = typeof(MyType).GetImplicitOperatorMethods(); var explicits = typeof(MyType).GetExplicitOperatorMethods(); ``` ### GetImplicitCastMethodTo / GetExplicitCastMethodTo Finds a specific conversion operator to a target type: ```csharp MethodInfo? cast = typeof(MyType).GetImplicitCastMethodTo(); if (cast != null) { string result = (string)cast.Invoke(null, new object[] { myInstance })!; } ``` ## Equals / NotEquals Type-safe equality checks: ```csharp typeof(string).Equals(); // true typeof(string).NotEquals(); // true ``` ## Interface Discovery ### GetDirectInterfaces Returns only the interfaces directly implemented by a type, excluding those inherited from base types or other interfaces: ```csharp // interface IA { } // interface IB : IA { } // class Base : IA { } // class Derived : Base, IB { } typeof(Derived).GetDirectInterfaces(); // Returns: [IB] — excludes IA (inherited via Base and IB) typeof(Base).GetDirectInterfaces(); // Returns: [IA] ``` ## Filtering Collections of Types When working with `IEnumerable`, use the filtering extensions: ```csharp var types = assembly.GetTypes(); // Filter by attribute var serializable = types.WithAttribute(); // Filter by attribute, returning the attribute instance var withAttrs = types.WithAttributeExpanded(); foreach (var (type, attr) in withAttrs) { Console.WriteLine($"{type.Name}: {attr.Message}"); } // Filter generic types var lists = types.WhichIsGenericTypeOf>(); // Filter by inheritance var derived = types.WhichInheritFromClass(); ``` --- --- url: /guide/type-parsing.md --- # Type Parsing `TypeHelper` resolves .NET types from human-readable string representations. It handles generics, arrays, C# keywords, and custom type mappings — with thread-safe caching for performance. ## FindType Resolves a type name string to a `System.Type`: ```csharp using Cocoar.Reflectensions; // C# keywords var stringType = TypeHelper.FindType("string"); // typeof(string) var intType = TypeHelper.FindType("int"); // typeof(int) var dynamicType = TypeHelper.FindType("dynamic"); // typeof(object) // Fully qualified names var dateType = TypeHelper.FindType("System.DateTime"); // Generic types var listType = TypeHelper.FindType("List"); var dictType = TypeHelper.FindType("Dictionary"); // Nested generics var complex = TypeHelper.FindType( "Dictionary>>"); // CLR backtick syntax var clrType = TypeHelper.FindType( "System.Collections.Generic.Dictionary`2[System.String, System.Int32]"); // Arrays var arrayType = TypeHelper.FindType("int[]"); var jaggedType = TypeHelper.FindType("int[][]"); ``` Returns `null` if the type cannot be found in any loaded assembly. ## Custom Type Mappings Map external type systems (e.g. TypeScript) to C# types: ```csharp var mapping = new Dictionary { ["number"] = "double", ["boolean"] = "bool", ["any"] = "object", ["Date"] = "System.DateTime" }; var type = TypeHelper.FindType("Dictionary", mapping); // Resolves to: typeof(Dictionary) ``` This is useful for code generation, plugin systems, or any scenario where type names come from a different language or configuration format. ## NormalizeTypeName Converts a type name string to the CLR-compatible format without resolving it: ```csharp var input = "Dictionary>"; var output = TypeHelper.NormalizeTypeName(input); // "System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.Int32]]" ``` This is the first step of `FindType`. Use it when you need the normalized string but not the actual `Type` object. ```csharp // With custom mapping var mapping = new Dictionary { ["number"] = "double" }; var normalized = TypeHelper.NormalizeTypeName("List", mapping); // "System.Collections.Generic.List`1[System.Double]" ``` ## Supported Formats | Format | Example | |--------|---------| | C# keywords | `string`, `int`, `double`, `bool`, `dynamic`, `object` | | Simple names | `Guid`, `DateTime`, `TimeSpan` | | Fully qualified | `System.Collections.Generic.List\`1`| | Generic (angle brackets) |`List`, `Dictionary\`| | Generic (CLR backtick) |`Dictionary\`2\[System.String, System.Int32]`| | Nested generics |`Dictionary\>>`| | Arrays |`int\[]`, `int\[]\[]`, `System.Object\[]\[]\[]`| | Mixed formats |`Dictionary\\` | ::: info Namespace Inference If a type name has no namespace (e.g. `Guid[]`), TypeHelper assumes it is in the `System` namespace. ::: ## Caching Type lookups are cached with thread-safe locking. The first call resolves and caches the type; subsequent calls return instantly. ```csharp // First call: resolves + caches var type1 = TypeHelper.FindType("Dictionary"); // Second call: cache hit (~0ns overhead) var type2 = TypeHelper.FindType("Dictionary"); ``` ## HasInspectableBaseType Checks whether a type has a base type worth inspecting (i.e., not `object`, `ValueType`, or `Enum`): ```csharp TypeHelper.HasInspectableBaseType(typeof(MyDerivedClass)); // true TypeHelper.HasInspectableBaseType(typeof(object)); // false TypeHelper.HasInspectableBaseType(typeof(int)); // false (ValueType) ``` --- --- url: /guide/utility-extensions.md --- # Utility Extensions The core package includes extensions for enums, DateTime, arrays, dictionaries, assemblies, and tasks. ## Enum Extensions ### GetName Get the display name of an enum value. Uses `[EnumMember]` or `[Description]` attributes if present, otherwise falls back to the member name: ```csharp public enum Status { [EnumMember(Value = "in_progress")] InProgress, [Description("Completed successfully")] Done, Pending } Status.InProgress.GetName(); // "in_progress" Status.Done.GetName(); // "Completed successfully" Status.Pending.GetName(); // "Pending" ``` ### TryFind Find an enum value from a string, checking member names and attribute values: ```csharp // Non-generic — accepts Type parameter, returns object if (EnumExtensions.TryFind(typeof(Status), "in_progress", out var status)) { var s = (Status)status!; // s == Status.InProgress } // Case-insensitive EnumExtensions.TryFind(typeof(Status), "PENDING", ignoreCase: true, out var s2); ``` ### Generic Enum Helper For a cleaner generic API, use `Enum`: ```csharp using Cocoar.Reflectensions.Helper; // Find enum value from string (returns default if not found) Status status = Enum.Find("InProgress"); // Try find if (Enum.TryFind("Done", out var result)) { // use result } // Case-insensitive if (Enum.TryFind("done", ignoreCase: true, out var result2)) { // use result2 } ``` ## DateTime Extensions ### Unix Time Conversion ```csharp DateTime now = DateTime.UtcNow; // To Unix time long unixMs = now.ToUnixTimeMilliseconds(); // From Unix time DateTime fromMs = DateTimeExtensions.FromUnixTimeMilliseconds(unixMs); DateTime fromSec = DateTimeExtensions.FromUnixTimeSeconds(1616345417); ``` ### Kind Management ```csharp DateTime dt = DateTime.Now; DateTime utc = dt.ToUtc(); // Converts to UTC DateTime local = dt.ToLocal(); // Converts to local DateTime withKind = dt.SetKind(DateTimeKind.Utc); // Sets kind without conversion DateTime asUtc = dt.SetIsUtc(); // Sets kind to UTC DateTime asLocal = dt.SetIsLocal(); // Sets kind to Local ``` ## Array Extensions ### Concat Concatenate two arrays: ```csharp int[] a = [1, 2, 3]; int[] b = [4, 5, 6]; int[] combined = a.Concat(b); // [1, 2, 3, 4, 5, 6] ``` ### SubArray Extract a portion of an array: ```csharp int[] arr = [1, 2, 3, 4, 5]; int[] sub1 = arr.SubArray(1, 3); // [2, 3, 4] (start index, length) int[] sub2 = arr.SubArray(2); // [3, 4, 5] (from index to end) ``` ### ArrayHelpers Static helpers for array operations: ```csharp using Cocoar.Reflectensions.Helper; int[] combined = ArrayHelpers.ConcatArrays(array1, array2); int[] sub = ArrayHelpers.SubArray(source, startIndex, length); ``` ## Dictionary Extensions ### Merge Merge two dictionaries, with the second overwriting duplicates: ```csharp var dict1 = new Dictionary { ["a"] = 1, ["b"] = 2 }; var dict2 = new Dictionary { ["b"] = 3, ["c"] = 4 }; var merged = dict1.Merge(dict2); // { "a": 1, "b": 3, "c": 4 } ``` ### Typed Value Access Extensions on `IDictionary` for type-safe value retrieval: ```csharp var data = new Dictionary { ["count"] = 42, ["name"] = "Alice" }; // GetValueAs — uses As (direct cast) int count = data.GetValueAs("count"); string name = data.GetValueAs("name", "default"); // TryGetValueAs — safe variant if (data.TryGetValueAs("count", out var n)) { // n == 42 } // GetValueTo — uses To (smart conversion) string countStr = data.GetValueTo("count"); // "42" // TryGetValueTo — safe variant if (data.TryGetValueTo("count", out var s)) { // s == "42" } ``` ::: tip `GetValueAs` uses direct casting (`As`), while `GetValueTo` uses smart conversion (`To`) with implicit operators and Parse methods. Use `As` when the types match, `To` when conversion is needed. ::: ## Assembly Extensions Read embedded resources from assemblies: ```csharp using Cocoar.Reflectensions; var assembly = typeof(MyClass).Assembly; // Read as stream Stream? stream = assembly.ReadResourceAsStream("MyNamespace.data.json"); // Read as string string? content = assembly.ReadResourceAsString("MyNamespace.template.html"); // Read as byte array byte[]? bytes = assembly.ReadResourceAsByteArray("MyNamespace.image.png"); ``` ## Task Extensions ### CastToTaskOf Convert a `Task` to `Task` using reflection (useful when the runtime type is known but the compile-time type is not): ```csharp Task untypedTask = GetSomeTask(); // actually returns Task Task typedTask = untypedTask.CastToTaskOf(); string result = await typedTask; ``` ## Wildcard Helper Pattern matching with wildcard syntax: ```csharp using Cocoar.Reflectensions.Helper; // Convert wildcard to regex string regex = WildcardHelper.WildcardToRegex("*.txt"); // Match with wildcards bool match = WildcardHelper.Match("hello.txt", "*.txt"); // true // Check if string contains wildcards bool hasWild = WildcardHelper.ContainsWildcard("test*"); // true ``` Supported wildcards: `*` (any characters), `?` (single character), `+` (one or more characters). ## Base58 Helper Encode and decode byte arrays in Base58 format: ```csharp using Cocoar.Reflectensions.Helper; byte[] data = Encoding.UTF8.GetBytes("Hello"); // Basic encode/decode string encoded = Base58Helper.Encode(data); byte[] decoded = Base58Helper.Decode(encoded); // With SHA256 checksum string withCheck = Base58Helper.EncodeWithCheckSum(data); byte[] verified = Base58Helper.DecodeWithCheckSum(withCheck); ``` ## Action Extensions Extension methods on `Action` that auto-instantiate parameters: ```csharp using Cocoar.Reflectensions; // Creates a new MyConfig instance, passes it to the action, returns it Action configure = config => { config.Setting = "value"; }; MyConfig result = configure.InvokeAction(); // With multiple parameters Action setup = (config, options) => { config.Name = "App"; options.Timeout = 30; }; var (config, options) = setup.InvokeAction(); ``` --- --- url: /guide/why-cocoar-reflectensions.md --- # Why Cocoar.Reflectensions? .NET reflection is powerful, but the raw APIs are verbose and error-prone. Reflectensions makes reflection code **readable**, **maintainable**, and **efficient**. ## The Problem Consider finding all methods named `Process` that return `Task` and accept a `string` parameter: **Raw .NET Reflection:** ```csharp var methods = typeof(MyService).GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == "Process") .Where(m => m.ReturnType == typeof(Task) || (m.ReturnType.IsGenericType && m.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))) .Where(m => { var parameters = m.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(string); }) .ToList(); ``` **With Reflectensions:** ```csharp var methods = typeof(MyService).GetMethods() .WithName("Process") .WithReturnType() .WithParametersOfType(typeof(string)); ``` Same result. One-third the code. Self-documenting. ## Type Resolution from Strings Resolving generic type names from configuration or user input requires significant boilerplate with raw .NET: **Raw .NET:** ```csharp // Good luck parsing "Dictionary>" by hand var typeName = "System.Collections.Generic.Dictionary`2[" + "[System.String, System.Private.CoreLib]," + "[System.Collections.Generic.List`1[" + "[System.Int32, System.Private.CoreLib]" + "], System.Private.CoreLib]" + "], System.Private.CoreLib]"; var type = Type.GetType(typeName); ``` **With Reflectensions:** ```csharp var type = TypeHelper.FindType("Dictionary>"); ``` Reflectensions normalizes human-readable type names to CLR-compatible format, resolves them across loaded assemblies, and caches the result for performance. ## Type Conversion Converting between types in .NET involves a cascade of checks and fallbacks: **Raw .NET:** ```csharp object value = "42"; int result; if (value is int i) result = i; else if (value is string s && int.TryParse(s, out var parsed)) result = parsed; else if (value is IConvertible convertible) result = convertible.ToInt32(null); else result = defaultValue; ``` **With Reflectensions:** ```csharp int result = value.Reflect().To(defaultValue); ``` The library automatically tries direct casting, implicit operators, `Parse`/`TryParse`, and registered conversion extensions — with fast paths for common type pairs. ## Type Checking Checking type relationships often requires nested conditions: **Raw .NET:** ```csharp bool isNumeric = type == typeof(int) || type == typeof(long) || type == typeof(float) || type == typeof(double) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) || type == typeof(uint) || type == typeof(ulong) || type == typeof(ushort) || type == typeof(sbyte); bool isDictionary = type.GetInterfaces() .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)); ``` **With Reflectensions:** ```csharp bool isNumeric = type.IsNumericType(); bool isDictionary = type.IsDictionaryType(); ``` ## Implicit Operator Conversion — Automatically This is one of the most powerful features. When your types define `implicit operator`, Reflectensions **finds and uses them automatically** during conversion — no configuration, no registration, no manual lookup. **Raw .NET:** ```csharp // You have to know the operator exists, find it, and invoke it var method = typeof(Truck).GetMethods(BindingFlags.Public | BindingFlags.Static) .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == typeof(Truck) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Camaro)); Truck? truck = method != null ? (Truck)method.Invoke(null, new object[] { camaro })! : null; ``` **With Reflectensions:** ```csharp Truck truck = camaro.Reflect().To(); ``` One line. Reflectensions searches for `op_Implicit` on **both** the source and target types, so it works regardless of which side defines the operator. Combined with the numeric widening table and `IConvertible` fallbacks, `To()` handles virtually any conversion scenario you throw at it. See the [Smart Type Conversion](/guide/type-conversion#automatic-implicit-operator-conversion) guide for full details. ## What You Get | Feature | Raw .NET | Reflectensions | |---------|----------|----------------| | Type parsing from strings | Manual CLR syntax | `TypeHelper.FindType("Dict")` | | Method filtering | Nested LINQ with GetParameters | `.WithName().WithReturnType()` | | Type checking | Long boolean chains | `.IsNumericType()`, `.IsDictionaryType()` | | Object conversion | Try/catch cascades | `.Reflect().To(fallback)` | | Implicit operator conversion | Manual op\_Implicit lookup + invoke | `.Reflect().To()` — automatic | | Operator discovery | Search op\_Implicit manually | `.GetImplicitCastMethodTo()` | | Property access by name | GetProperty + GetValue + null checks | `.Reflect().GetPropertyValue("Name")` | ## Zero Dependencies Reflectensions has no external NuGet dependencies. The core library is entirely self-contained, built on .NET 8.0 and .NET Standard 2.0 for maximum compatibility.