/src/Avalonia.Base/Utilities/TypeUtilities.cs
C# | 339 lines | 249 code | 35 blank | 55 comment | 54 complexity | f4d03e58bb3c603a8540ce1b89f88aa1 MD5 | raw file
- // Copyright (c) The Avalonia Project. All rights reserved.
- // Licensed under the MIT license. See licence.md file in the project root for full license information.
- using System;
- using System.Globalization;
- using System.Linq;
- using System.Reflection;
- namespace Avalonia.Utilities
- {
- /// <summary>
- /// Provides utilities for working with types at runtime.
- /// </summary>
- public static class TypeUtilities
- {
- private static int[] Conversions =
- {
- 0b101111111111101, // Boolean
- 0b100001111111110, // Char
- 0b101111111111111, // SByte
- 0b101111111111111, // Byte
- 0b101111111111111, // Int16
- 0b101111111111111, // UInt16
- 0b101111111111111, // Int32
- 0b101111111111111, // UInt32
- 0b101111111111111, // Int64
- 0b101111111111111, // UInt64
- 0b101111111111101, // Single
- 0b101111111111101, // Double
- 0b101111111111101, // Decimal
- 0b110000000000000, // DateTime
- 0b111111111111111, // String
- };
- private static int[] ImplicitConversions =
- {
- 0b000000000000001, // Boolean
- 0b001110111100010, // Char
- 0b001110101010100, // SByte
- 0b001111111111000, // Byte
- 0b001110101010000, // Int16
- 0b001111111100000, // UInt16
- 0b001110101000000, // Int32
- 0b001111110000000, // UInt32
- 0b001110100000000, // Int64
- 0b001111000000000, // UInt64
- 0b000110000000000, // Single
- 0b000100000000000, // Double
- 0b001000000000000, // Decimal
- 0b010000000000000, // DateTime
- 0b100000000000000, // String
- };
- private static Type[] InbuiltTypes =
- {
- typeof(Boolean),
- typeof(Char),
- typeof(SByte),
- typeof(Byte),
- typeof(Int16),
- typeof(UInt16),
- typeof(Int32),
- typeof(UInt32),
- typeof(Int64),
- typeof(UInt64),
- typeof(Single),
- typeof(Double),
- typeof(Decimal),
- typeof(DateTime),
- typeof(String),
- };
- private static readonly Type[] NumericTypes = new[]
- {
- typeof(Byte),
- typeof(Decimal),
- typeof(Double),
- typeof(Int16),
- typeof(Int32),
- typeof(Int64),
- typeof(SByte),
- typeof(Single),
- typeof(UInt16),
- typeof(UInt32),
- typeof(UInt64),
- };
- /// <summary>
- /// Returns a value indicating whether null can be assigned to the specified type.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns>True if the type accepts null values; otherwise false.</returns>
- public static bool AcceptsNull(Type type)
- {
- var t = type.GetTypeInfo();
- return !t.IsValueType || (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(Nullable<>)));
- }
- /// <summary>
- /// Try to convert a value to a type by any means possible.
- /// </summary>
- /// <param name="to">The type to cast to.</param>
- /// <param name="value">The value to cast.</param>
- /// <param name="culture">The culture to use.</param>
- /// <param name="result">If successful, contains the cast value.</param>
- /// <returns>True if the cast was successful, otherwise false.</returns>
- public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
- {
- if (value == null)
- {
- result = null;
- return AcceptsNull(to);
- }
- if (value == AvaloniaProperty.UnsetValue)
- {
- result = value;
- return true;
- }
- var from = value.GetType();
- var fromTypeInfo = from.GetTypeInfo();
- var toTypeInfo = to.GetTypeInfo();
- if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
- {
- result = value;
- return true;
- }
- if (to == typeof(string))
- {
- result = Convert.ToString(value);
- return true;
- }
- if (toTypeInfo.IsEnum && from == typeof(string))
- {
- if (Enum.IsDefined(to, (string)value))
- {
- result = Enum.Parse(to, (string)value);
- return true;
- }
- }
- if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum)
- {
- result = null;
- if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue))
- {
- result = Enum.ToObject(to, enumValue);
- return true;
- }
- }
- if (fromTypeInfo.IsEnum && IsNumeric(to))
- {
- try
- {
- result = Convert.ChangeType((int)value, to, culture);
- return true;
- }
- catch
- {
- result = null;
- return false;
- }
- }
- var convertableFrom = Array.IndexOf(InbuiltTypes, from);
- var convertableTo = Array.IndexOf(InbuiltTypes, to);
- if (convertableFrom != -1 && convertableTo != -1)
- {
- if ((Conversions[convertableFrom] & 1 << convertableTo) != 0)
- {
- try
- {
- result = Convert.ChangeType(value, to, culture);
- return true;
- }
- catch
- {
- result = null;
- return false;
- }
- }
- }
- var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
- if (cast != null)
- {
- result = cast.Invoke(null, new[] { value });
- return true;
- }
- result = null;
- return false;
- }
- /// <summary>
- /// Try to convert a value to a type using the implicit conversions allowed by the C#
- /// language.
- /// </summary>
- /// <param name="to">The type to cast to.</param>
- /// <param name="value">The value to cast.</param>
- /// <param name="result">If successful, contains the cast value.</param>
- /// <returns>True if the cast was successful, otherwise false.</returns>
- public static bool TryConvertImplicit(Type to, object value, out object result)
- {
- if (value == null)
- {
- result = null;
- return AcceptsNull(to);
- }
- if (value == AvaloniaProperty.UnsetValue)
- {
- result = value;
- return true;
- }
- var from = value.GetType();
- var fromTypeInfo = from.GetTypeInfo();
- var toTypeInfo = to.GetTypeInfo();
- if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
- {
- result = value;
- return true;
- }
- var convertableFrom = Array.IndexOf(InbuiltTypes, from);
- var convertableTo = Array.IndexOf(InbuiltTypes, to);
- if (convertableFrom != -1 && convertableTo != -1)
- {
- if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0)
- {
- try
- {
- result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture);
- return true;
- }
- catch
- {
- result = null;
- return false;
- }
- }
- }
- var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
- if (cast != null)
- {
- result = cast.Invoke(null, new[] { value });
- return true;
- }
- result = null;
- return false;
- }
- /// <summary>
- /// Convert a value to a type by any means possible, returning the default for that type
- /// if the value could not be converted.
- /// </summary>
- /// <param name="value">The value to cast.</param>
- /// <param name="type">The type to cast to..</param>
- /// <param name="culture">The culture to use.</param>
- /// <returns>A value of <paramref name="type"/>.</returns>
- public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
- {
- return TryConvert(type, value, culture, out object result) ? result : Default(type);
- }
- /// <summary>
- /// Convert a value to a type using the implicit conversions allowed by the C# language or
- /// return the default for the type if the value could not be converted.
- /// </summary>
- /// <param name="value">The value to cast.</param>
- /// <param name="type">The type to cast to..</param>
- /// <returns>A value of <paramref name="type"/>.</returns>
- public static object ConvertImplicitOrDefault(object value, Type type)
- {
- return TryConvertImplicit(type, value, out object result) ? result : Default(type);
- }
- /// <summary>
- /// Gets the default value for the specified type.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns>The default value.</returns>
- public static object Default(Type type)
- {
- var typeInfo = type.GetTypeInfo();
- if (typeInfo.IsValueType)
- {
- return Activator.CreateInstance(type);
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// Determines if a type is numeric. Nullable numeric types are considered numeric.
- /// </summary>
- /// <returns>
- /// True if the type is numeric; otherwise false.
- /// </returns>
- /// <remarks>
- /// Boolean is not considered numeric.
- /// </remarks>
- public static bool IsNumeric(Type type)
- {
- if (type == null)
- {
- return false;
- }
- if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- return IsNumeric(Nullable.GetUnderlyingType(type));
- }
- else
- {
- return NumericTypes.Contains(type);
- }
- }
- }
- }