#nullable enable
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
namespace T3.Core.Compilation;
///
/// Just a helper class to create instances of T based on a type.
/// Provides "constructors" (Func<T>) for types that are not known at runtime that are considerably faster than Activator.CreateInstance.
///
/// The type to create instances of
public sealed class GenericFactory
{
private readonly Type _defaultType;
private readonly Type? _enumType;
private readonly ConcurrentDictionary> _entries = new();
public GenericFactory(Type? defaultType, Type? enumType = null)
{
if(defaultType == null || !defaultType.IsAssignableTo(typeof(T)))
throw new ArgumentException("defaultType must be a subclass of T", nameof(defaultType));
_defaultType = defaultType;
_enumType = enumType;
}
public T CreateFor(Type type)
{
if (_entries.TryGetValue(type, out var factory))
{
return factory();
}
factory = AddFactory(type, null);
return factory();
}
public Func AddFactory(Type type, Func? constructor)
{
constructor ??= CreateFactoryFor(type);
_entries[type] = constructor;
return constructor;
}
private Func CreateFactoryFor(Type type)
{
Type genericType;
if(_enumType != null && type.IsEnum)
{
genericType = _enumType.MakeGenericType(type);
}
else
{
genericType = _defaultType.MakeGenericType(type);
}
var compiledConstructor = Expression.Lambda>(Expression.New(genericType)).Compile();
return () => (T)compiledConstructor();
}
}