diff options
Diffstat (limited to 'ThirdParty/CsvHelper-master/src/CsvHelper/ObjectCreator.cs')
-rw-r--r-- | ThirdParty/CsvHelper-master/src/CsvHelper/ObjectCreator.cs | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/ObjectCreator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/ObjectCreator.cs new file mode 100644 index 0000000..4e1789e --- /dev/null +++ b/ThirdParty/CsvHelper-master/src/CsvHelper/ObjectCreator.cs @@ -0,0 +1,218 @@ +// Copyright 2009-2022 Josh Close +// This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0. +// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0. +// https://github.com/JoshClose/CsvHelper +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace CsvHelper +{ + /// <summary> + /// Efficiently creates instances of object types. + /// </summary> + public class ObjectCreator + { + private readonly Dictionary<int, Func<object[], object>> cache = new Dictionary<int, Func<object[], object>>(); + + /// <summary> + /// Creates an instance of type T using the given arguments. + /// </summary> + /// <typeparam name="T">The type to create an instance of.</typeparam> + /// <param name="args">The constrcutor arguments.</param> + public T CreateInstance<T>(params object[] args) + { + return (T)CreateInstance(typeof(T), args); + } + + /// <summary> + /// Creates an instance of the given type using the given arguments. + /// </summary> + /// <param name="type">The type to create an instance of.</param> + /// <param name="args">The constructor arguments.</param> + public object CreateInstance(Type type, params object[] args) + { + var func = GetFunc(type, args); + + return func(args); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Func<object[], object> GetFunc(Type type, object[] args) + { + var argTypes = GetArgTypes(args); + var key = GetConstructorCacheKey(type, argTypes); + if (!cache.TryGetValue(key, out var func)) + { + cache[key] = func = CreateInstanceFunc(type, argTypes); + } + + return func; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Type[] GetArgTypes(object[] args) + { + var argTypes = new Type[args.Length]; + for (var i = 0; i < args.Length; i++) + { + argTypes[i] = args[i]?.GetType() ?? typeof(object); + } + + return argTypes; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetConstructorCacheKey(Type type, Type[] args) + { +#if !NET45 + var hashCode = new HashCode(); + hashCode.Add(type.GetHashCode()); + for (var i = 0; i < args.Length; i++) + { + hashCode.Add(args[i].GetHashCode()); + } + + return hashCode.ToHashCode(); +#else + unchecked + { + var hash = 17; + hash = hash * 31 + type.GetHashCode(); + for (var i = 0; i < args.Length; i++) + { + hash = hash * 31 + (args[i].GetHashCode()); + } + + return hash; + } +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Func<object[], object> CreateInstanceFunc(Type type, Type[] argTypes) + { + var parameterExpression = Expression.Parameter(typeof(object[]), "args"); + + Expression body; + if (type.IsValueType) + { + if (argTypes.Length > 0) + { + throw GetConstructorNotFoundException(type, argTypes); + } + + body = Expression.Convert(Expression.Default(type), typeof(object)); + } + else + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var constructor = GetConstructor(constructors, type, argTypes); + + var parameters = constructor.GetParameters(); + var parameterTypes = new Type[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) + { + parameterTypes[i] = parameters[i].ParameterType; + } + + var arguments = new List<Expression>(); + for (var i = 0; i < parameterTypes.Length; i++) + { + var parameterType = parameterTypes[i]; + var arrayIndexExpression = Expression.ArrayIndex(parameterExpression, Expression.Constant(i)); + var convertExpression = Expression.Convert(arrayIndexExpression, parameterType); + arguments.Add(convertExpression); + } + + body = Expression.New(constructor, arguments); + } + + var lambda = Expression.Lambda<Func<object[], object>>(body, new[] { parameterExpression }); + var func = lambda.Compile(); + + return func; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ConstructorInfo GetConstructor(ConstructorInfo[] constructors, Type type, Type[] argTypes) + { + var matchType = MatchType.Exact; + var fuzzyMatches = new List<ConstructorInfo>(); + for (var i = 0; i < constructors.Length; i++) + { + var constructor = constructors[i]; + var parameters = constructors[i].GetParameters(); + + if (parameters.Length != argTypes.Length) + { + continue; + } + + for (var j = 0; j < parameters.Length && j < argTypes.Length; j++) + { + var parameterType = parameters[j].ParameterType; + var argType = argTypes[j]; + + if (argType == parameterType) + { + matchType = MatchType.Exact; + continue; + } + + if (!parameterType.IsValueType && (parameterType.IsAssignableFrom(argType) || argType == typeof(object))) + { + matchType = MatchType.Fuzzy; + continue; + } + + matchType = MatchType.None; + break; + } + + if (matchType == MatchType.Exact) + { + // Only possible to have one exact match. + return constructor; + } + + if (matchType == MatchType.Fuzzy) + { + fuzzyMatches.Add(constructor); + } + } + + if (fuzzyMatches.Count == 1) + { + return fuzzyMatches[0]; + } + + if (fuzzyMatches.Count > 1) + { + throw new AmbiguousMatchException(); + } + + throw GetConstructorNotFoundException(type, argTypes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static MissingMethodException GetConstructorNotFoundException(Type type, Type[] argTypes) + { + var signature = $"{type.FullName}({string.Join(", ", argTypes.Select(a => a.FullName))})"; + + throw new MissingMethodException($"Constructor '{signature}' was not found."); + } + + private enum MatchType + { + None = 0, + Exact = 1, + Fuzzy = 2 + } + } +} |