From 635819b89f5207b50497ecd3202f40ec8dd1ee03 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 10 Oct 2016 20:26:01 -0300 Subject: [PATCH] Reduced command module lifetime to a single command execution. Removed ModuleAttribute. --- .../Attributes/DontAutoLoadAttribute.cs | 9 ++ .../Attributes/InAttribute.cs | 8 -- .../Attributes/ModuleAttribute.cs | 22 ---- .../Attributes/PreconditionAttribute.cs | 2 +- .../Preconditions/RequireContextAttribute.cs | 2 +- .../RequirePermissionAttribute.cs | 2 +- .../{Command.cs => CommandInfo.cs} | 46 +++---- src/Discord.Net.Commands/CommandParser.cs | 2 +- src/Discord.Net.Commands/CommandService.cs | 122 +++++++++--------- src/Discord.Net.Commands/Map/CommandMap.cs | 8 +- .../Map/CommandMapNode.cs | 10 +- src/Discord.Net.Commands/ModuleBase.cs | 15 +++ .../{Module.cs => ModuleInfo.cs} | 41 +++--- src/Discord.Net.Commands/ReflectionUtils.cs | 24 ++-- .../Results/SearchResult.cs | 6 +- .../Utils/ConcurrentHashSet.cs | 2 +- 16 files changed, 161 insertions(+), 160 deletions(-) create mode 100644 src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs delete mode 100644 src/Discord.Net.Commands/Attributes/InAttribute.cs delete mode 100644 src/Discord.Net.Commands/Attributes/ModuleAttribute.cs rename src/Discord.Net.Commands/{Command.cs => CommandInfo.cs} (87%) create mode 100644 src/Discord.Net.Commands/ModuleBase.cs rename src/Discord.Net.Commands/{Module.cs => ModuleInfo.cs} (62%) diff --git a/src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs b/src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs new file mode 100644 index 000000000..d6a1c646e --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/DontAutoLoadAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Class)] + public class DontAutoLoadAttribute : Attribute + { + } +} diff --git a/src/Discord.Net.Commands/Attributes/InAttribute.cs b/src/Discord.Net.Commands/Attributes/InAttribute.cs deleted file mode 100644 index 0d4fd9eb0..000000000 --- a/src/Discord.Net.Commands/Attributes/InAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Discord.Commands -{ - public class InAttribute : Attribute - { - } -} diff --git a/src/Discord.Net.Commands/Attributes/ModuleAttribute.cs b/src/Discord.Net.Commands/Attributes/ModuleAttribute.cs deleted file mode 100644 index 297cce84c..000000000 --- a/src/Discord.Net.Commands/Attributes/ModuleAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Discord.Commands -{ - [AttributeUsage(AttributeTargets.Class)] - public class ModuleAttribute : Attribute - { - public string Prefix { get; } - public bool AutoLoad { get; set; } - - public ModuleAttribute() - { - Prefix = null; - AutoLoad = true; - } - public ModuleAttribute(string prefix) - { - Prefix = prefix; - AutoLoad = true; - } - } -} diff --git a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs index 90e82607e..067c8e93b 100644 --- a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs @@ -6,6 +6,6 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public abstract class PreconditionAttribute : Attribute { - public abstract Task CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance); + public abstract Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map); } } diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index c14dbd5bf..1cd32e72e 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -21,7 +21,7 @@ namespace Discord.Commands Contexts = contexts; } - public override Task CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance) + public override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) { bool isValid = false; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs index 3583836d8..26aeac5ec 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs @@ -20,7 +20,7 @@ namespace Discord.Commands GuildPermission = null; } - public override Task CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance) + public override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) { var guildUser = context.User as IGuildUser; diff --git a/src/Discord.Net.Commands/Command.cs b/src/Discord.Net.Commands/CommandInfo.cs similarity index 87% rename from src/Discord.Net.Commands/Command.cs rename to src/Discord.Net.Commands/CommandInfo.cs index b1ec3f0e1..e63fb6d3f 100644 --- a/src/Discord.Net.Commands/Command.cs +++ b/src/Discord.Net.Commands/CommandInfo.cs @@ -10,16 +10,15 @@ using System.Threading.Tasks; namespace Discord.Commands { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class Command + public class CommandInfo { - private static readonly MethodInfo _convertParamsMethod = typeof(Command).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList)); + private static readonly MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList)); private static readonly ConcurrentDictionary, object>> _arrayConverters = new ConcurrentDictionary, object>>(); - - private readonly object _instance; - private readonly Func, Task> _action; + + private readonly Func _action; public MethodInfo Source { get; } - public Module Module { get; } + public ModuleInfo Module { get; } public string Name { get; } public string Summary { get; } public string Remarks { get; } @@ -30,13 +29,12 @@ namespace Discord.Commands public IReadOnlyList Parameters { get; } public IReadOnlyList Preconditions { get; } - internal Command(MethodInfo source, Module module, object instance, CommandAttribute attribute, string groupPrefix) + internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix) { try { Source = source; Module = module; - _instance = instance; Name = source.Name; @@ -85,18 +83,18 @@ namespace Discord.Commands } } - public async Task CheckPreconditions(CommandContext context) + public async Task CheckPreconditions(CommandContext context, IDependencyMap map = null) { foreach (PreconditionAttribute precondition in Module.Preconditions) { - var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false); + var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false); if (!result.IsSuccess) return result; } foreach (PreconditionAttribute precondition in Preconditions) { - var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false); + var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false); if (!result.IsSuccess) return result; } @@ -169,11 +167,9 @@ namespace Discord.Commands private IReadOnlyList BuildParameters(MethodInfo methodInfo) { var parameters = methodInfo.GetParameters(); - if (parameters.Length == 0 || parameters[0].ParameterType != typeof(CommandContext)) - throw new InvalidOperationException($"The first parameter of a command must be {nameof(CommandContext)}."); - var paramBuilder = ImmutableArray.CreateBuilder(parameters.Length - 1); - for (int i = 1; i < parameters.Length; i++) + var paramBuilder = ImmutableArray.CreateBuilder(parameters.Length); + for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; var type = parameter.ParameterType; @@ -209,19 +205,23 @@ namespace Discord.Commands } return paramBuilder.ToImmutable(); } - private Func, Task> BuildAction(MethodInfo methodInfo) + private Func BuildAction(MethodInfo methodInfo) { if (methodInfo.ReturnType != typeof(Task)) throw new InvalidOperationException("Commands must return a non-generic Task."); - return (msg, args) => + return (context, args) => { - object[] newArgs = new object[args.Count + 1]; - newArgs[0] = msg; - for (int i = 0; i < args.Count; i++) - newArgs[i + 1] = args[i]; - var result = methodInfo.Invoke(_instance, newArgs); - return result as Task ?? Task.CompletedTask; + var instance = Module.CreateInstance(); + instance.Context = context; + try + { + return methodInfo.Invoke(instance, args) as Task ?? Task.CompletedTask; + } + finally + { + (instance as IDisposable)?.Dispose(); + } }; } diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index b87e67aa0..1808b705d 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -13,7 +13,7 @@ namespace Discord.Commands QuotedParameter } - public static async Task ParseArgs(Command command, CommandContext context, string input, int startPos) + public static async Task ParseArgs(CommandInfo command, CommandContext context, string input, int startPos) { CommandParameter curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 509237310..a76c7bdc3 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -11,18 +11,20 @@ namespace Discord.Commands { public class CommandService { + private static readonly TypeInfo _moduleTypeInfo = typeof(ModuleBase).GetTypeInfo(); + private readonly SemaphoreSlim _moduleLock; - private readonly ConcurrentDictionary _modules; + private readonly ConcurrentDictionary _moduleDefs; private readonly ConcurrentDictionary _typeReaders; private readonly CommandMap _map; - public IEnumerable Modules => _modules.Select(x => x.Value); - public IEnumerable Commands => _modules.SelectMany(x => x.Value.Commands); + public IEnumerable Modules => _moduleDefs.Select(x => x.Value); + public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Value.Commands); public CommandService() { _moduleLock = new SemaphoreSlim(1, 1); - _modules = new ConcurrentDictionary(); + _moduleDefs = new ConcurrentDictionary(); _map = new CommandMap(); _typeReaders = new ConcurrentDictionary { @@ -45,7 +47,6 @@ namespace Discord.Commands [typeof(IMessage)] = new MessageTypeReader(), [typeof(IUserMessage)] = new MessageTypeReader(), - //[typeof(ISystemMessage)] = new MessageTypeReader(), [typeof(IChannel)] = new ChannelTypeReader(), [typeof(IDMChannel)] = new ChannelTypeReader(), [typeof(IGroupChannel)] = new ChannelTypeReader(), @@ -55,120 +56,99 @@ namespace Discord.Commands [typeof(ITextChannel)] = new ChannelTypeReader(), [typeof(IVoiceChannel)] = new ChannelTypeReader(), - //[typeof(IGuild)] = new GuildTypeReader(), - [typeof(IRole)] = new RoleTypeReader(), - //[typeof(IInvite)] = new InviteTypeReader(), - //[typeof(IInviteMetadata)] = new InviteTypeReader(), - [typeof(IUser)] = new UserTypeReader(), [typeof(IGroupUser)] = new UserTypeReader(), [typeof(IGuildUser)] = new UserTypeReader(), }; } - public void AddTypeReader(TypeReader reader) - { - _typeReaders[typeof(T)] = reader; - } - public void AddTypeReader(Type type, TypeReader reader) - { - _typeReaders[type] = reader; - } - internal TypeReader GetTypeReader(Type type) - { - TypeReader reader; - if (_typeReaders.TryGetValue(type, out reader)) - return reader; - return null; - } - - public async Task Load(object moduleInstance) + //Modules + public async Task AddModule(IDependencyMap dependencyMap = null) { await _moduleLock.WaitAsync().ConfigureAwait(false); try { - if (_modules.ContainsKey(moduleInstance.GetType())) - throw new ArgumentException($"This module has already been loaded."); + if (_moduleDefs.ContainsKey(typeof(T))) + throw new ArgumentException($"This module has already been added."); - var typeInfo = moduleInstance.GetType().GetTypeInfo(); - var moduleAttr = typeInfo.GetCustomAttribute(); - if (moduleAttr == null) - throw new ArgumentException($"Modules must be marked with ModuleAttribute."); + var typeInfo = typeof(T).GetTypeInfo(); + if (!_moduleTypeInfo.IsAssignableFrom(typeInfo)) + throw new ArgumentException($"Modules must inherit ModuleBase."); - return LoadInternal(moduleInstance, moduleAttr, typeInfo, null); + return AddModuleInternal(typeInfo, dependencyMap); } finally { _moduleLock.Release(); } } - private Module LoadInternal(object moduleInstance, ModuleAttribute moduleAttr, TypeInfo typeInfo, IDependencyMap dependencyMap) - { - if (_modules.ContainsKey(moduleInstance.GetType())) - return _modules[moduleInstance.GetType()]; - - var loadedModule = new Module(typeInfo, this, moduleInstance, moduleAttr, dependencyMap); - _modules[moduleInstance.GetType()] = loadedModule; - - foreach (var cmd in loadedModule.Commands) - _map.AddCommand(cmd); - - return loadedModule; - } - public async Task> LoadAssembly(Assembly assembly, IDependencyMap dependencyMap = null) + public async Task> AddModules(Assembly assembly, IDependencyMap dependencyMap = null) { - var modules = ImmutableArray.CreateBuilder(); + var moduleDefs = ImmutableArray.CreateBuilder(); await _moduleLock.WaitAsync().ConfigureAwait(false); try { foreach (var type in assembly.ExportedTypes) { - var typeInfo = type.GetTypeInfo(); - var moduleAttr = typeInfo.GetCustomAttribute(); - if (moduleAttr != null && moduleAttr.AutoLoad) + if (!_moduleDefs.ContainsKey(type)) { - var moduleInstance = ReflectionUtils.CreateObject(typeInfo, this, dependencyMap); - modules.Add(LoadInternal(moduleInstance, moduleAttr, typeInfo, dependencyMap)); + var typeInfo = type.GetTypeInfo(); + if (_moduleTypeInfo.IsAssignableFrom(typeInfo)) + { + var dontAutoLoad = typeInfo.GetCustomAttribute(); + if (dontAutoLoad == null) + moduleDefs.Add(AddModuleInternal(typeInfo, dependencyMap)); + } } } - return modules.ToImmutable(); + return moduleDefs.ToImmutable(); } finally { _moduleLock.Release(); } } + private ModuleInfo AddModuleInternal(TypeInfo typeInfo, IDependencyMap dependencyMap) + { + var moduleDef = new ModuleInfo(typeInfo, this, dependencyMap); + _moduleDefs[typeInfo.BaseType] = moduleDef; + + foreach (var cmd in moduleDef.Commands) + _map.AddCommand(cmd); + + return moduleDef; + } - public async Task Unload(Module module) + public async Task RemoveModule(ModuleInfo module) { await _moduleLock.WaitAsync().ConfigureAwait(false); try { - return UnloadInternal(module.Instance); + return RemoveModuleInternal(module.Source.BaseType); } finally { _moduleLock.Release(); } } - public async Task Unload(object moduleInstance) + public async Task RemoveModule() { await _moduleLock.WaitAsync().ConfigureAwait(false); try { - return UnloadInternal(moduleInstance); + return RemoveModuleInternal(typeof(T)); } finally { _moduleLock.Release(); } } - private bool UnloadInternal(object module) + private bool RemoveModuleInternal(Type type) { - Module unloadedModule; - if (_modules.TryRemove(module.GetType(), out unloadedModule)) + ModuleInfo unloadedModule; + if (_moduleDefs.TryRemove(type, out unloadedModule)) { foreach (var cmd in unloadedModule.Commands) _map.RemoveCommand(cmd); @@ -178,6 +158,24 @@ namespace Discord.Commands return false; } + //Type Readers + public void AddTypeReader(TypeReader reader) + { + _typeReaders[typeof(T)] = reader; + } + public void AddTypeReader(Type type, TypeReader reader) + { + _typeReaders[type] = reader; + } + internal TypeReader GetTypeReader(Type type) + { + TypeReader reader; + if (_typeReaders.TryGetValue(type, out reader)) + return reader; + return null; + } + + //Execution public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); public SearchResult Search(CommandContext context, string input) { diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs index a5a1f8bc4..e809c1b70 100644 --- a/src/Discord.Net.Commands/Map/CommandMap.cs +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -16,7 +16,7 @@ namespace Discord.Commands _nodes = new ConcurrentDictionary(); } - public void AddCommand(Command command) + public void AddCommand(CommandInfo command) { foreach (string text in command.Aliases) { @@ -35,7 +35,7 @@ namespace Discord.Commands } } } - public void RemoveCommand(Command command) + public void RemoveCommand(CommandInfo command) { foreach (string text in command.Aliases) { @@ -60,7 +60,7 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(string text) + public IEnumerable GetCommands(string text) { int nextSpace = NextWhitespace(text); string name; @@ -76,7 +76,7 @@ namespace Discord.Commands if (_nodes.TryGetValue(name, out nextNode)) return nextNode.GetCommands(text, nextSpace + 1); else - return Enumerable.Empty(); + return Enumerable.Empty(); } } diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs index 5ef42544e..a348a8cb4 100644 --- a/src/Discord.Net.Commands/Map/CommandMapNode.cs +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -9,7 +9,7 @@ namespace Discord.Commands private readonly ConcurrentDictionary _nodes; private readonly string _name; private readonly object _lockObj = new object(); - private ImmutableArray _commands; + private ImmutableArray _commands; public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0; @@ -17,10 +17,10 @@ namespace Discord.Commands { _name = name; _nodes = new ConcurrentDictionary(); - _commands = ImmutableArray.Create(); + _commands = ImmutableArray.Create(); } - public void AddCommand(string text, int index, Command command) + public void AddCommand(string text, int index, CommandInfo command) { int nextSpace = text.IndexOf(' ', index); string name; @@ -41,7 +41,7 @@ namespace Discord.Commands } } } - public void RemoveCommand(string text, int index, Command command) + public void RemoveCommand(string text, int index, CommandInfo command) { int nextSpace = text.IndexOf(' ', index); string name; @@ -68,7 +68,7 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(string text, int index) + public IEnumerable GetCommands(string text, int index) { int nextSpace = text.IndexOf(' ', index); string name; diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs new file mode 100644 index 000000000..544e37f65 --- /dev/null +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Discord.Commands +{ + public abstract class ModuleBase + { + public IDiscordClient Client { get; internal set; } + public CommandContext Context { get; internal set; } + + protected virtual async Task ReplyAsync(string message, bool isTTS = false, RequestOptions options = null) + { + await Context.Channel.SendMessageAsync(message, isTTS, options).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.Commands/Module.cs b/src/Discord.Net.Commands/ModuleInfo.cs similarity index 62% rename from src/Discord.Net.Commands/Module.cs rename to src/Discord.Net.Commands/ModuleInfo.cs index 9d61ca522..c061e3de4 100644 --- a/src/Discord.Net.Commands/Module.cs +++ b/src/Discord.Net.Commands/ModuleInfo.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; @@ -6,26 +7,31 @@ using System.Reflection; namespace Discord.Commands { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class Module + public class ModuleInfo { + internal readonly Func _builder; + public TypeInfo Source { get; } public CommandService Service { get; } public string Name { get; } public string Prefix { get; } public string Summary { get; } public string Remarks { get; } - public IEnumerable Commands { get; } - internal object Instance { get; } - + public IEnumerable Commands { get; } public IReadOnlyList Preconditions { get; } - internal Module(TypeInfo source, CommandService service, object instance, ModuleAttribute moduleAttr, IDependencyMap dependencyMap) + internal ModuleInfo(TypeInfo source, CommandService service, IDependencyMap dependencyMap) { Source = source; Service = service; Name = source.Name; - Prefix = moduleAttr.Prefix ?? ""; - Instance = instance; + _builder = ReflectionUtils.CreateBuilder(source, Service, dependencyMap); + + var groupAttr = source.GetCustomAttribute(); + if (groupAttr != null) + Prefix = groupAttr.Prefix; + else + Prefix = ""; var nameAttr = source.GetCustomAttribute(); if (nameAttr != null) @@ -39,20 +45,19 @@ namespace Discord.Commands if (remarksAttr != null) Remarks = remarksAttr.Text; - List commands = new List(); - SearchClass(source, instance, commands, Prefix, dependencyMap); + List commands = new List(); + SearchClass(source, commands, Prefix, dependencyMap); Commands = commands; - Preconditions = BuildPreconditions(); + Preconditions = Source.GetCustomAttributes().ToImmutableArray(); } - - private void SearchClass(TypeInfo parentType, object instance, List commands, string groupPrefix, IDependencyMap dependencyMap) + private void SearchClass(TypeInfo parentType, List commands, string groupPrefix, IDependencyMap dependencyMap) { foreach (var method in parentType.DeclaredMethods) { var cmdAttr = method.GetCustomAttribute(); if (cmdAttr != null) - commands.Add(new Command(method, this, instance, cmdAttr, groupPrefix)); + commands.Add(new CommandInfo(method, this, cmdAttr, groupPrefix)); } foreach (var type in parentType.DeclaredNestedTypes) { @@ -66,15 +71,13 @@ namespace Discord.Commands else nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant(); - SearchClass(type, ReflectionUtils.CreateObject(type, Service, dependencyMap), commands, nextGroupPrefix, dependencyMap); + SearchClass(type, commands, nextGroupPrefix, dependencyMap); } } } - private IReadOnlyList BuildPreconditions() - { - return Source.GetCustomAttributes().ToImmutableArray(); - } + internal ModuleBase CreateInstance() + => _builder(); public override string ToString() => Name; private string DebuggerDisplay => Name; diff --git a/src/Discord.Net.Commands/ReflectionUtils.cs b/src/Discord.Net.Commands/ReflectionUtils.cs index 4de883731..e84a037ef 100644 --- a/src/Discord.Net.Commands/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/ReflectionUtils.cs @@ -6,7 +6,10 @@ namespace Discord.Commands { internal class ReflectionUtils { - internal static object CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) + internal static T CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) + => CreateBuilder(typeInfo, service, map)(); + + internal static Func CreateBuilder(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) { var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray(); if (constructors.Length == 0) @@ -14,7 +17,7 @@ namespace Discord.Commands else if (constructors.Length > 1) throw new InvalidOperationException($"Multiple constructors found for \"{typeInfo.FullName}\""); - var constructor = constructors[0]; + var constructor = constructors[0]; ParameterInfo[] parameters = constructor.GetParameters(); object[] args = new object[parameters.Length]; @@ -34,14 +37,17 @@ namespace Discord.Commands args[i] = arg; } - try - { - return constructor.Invoke(args); - } - catch (Exception ex) + return () => { - throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex); - } + try + { + return (T)constructor.Invoke(args); + } + catch (Exception ex) + { + throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex); + } + }; } } } diff --git a/src/Discord.Net.Commands/Results/SearchResult.cs b/src/Discord.Net.Commands/Results/SearchResult.cs index 962834c03..17942b61a 100644 --- a/src/Discord.Net.Commands/Results/SearchResult.cs +++ b/src/Discord.Net.Commands/Results/SearchResult.cs @@ -7,14 +7,14 @@ namespace Discord.Commands public struct SearchResult : IResult { public string Text { get; } - public IReadOnlyList Commands { get; } + public IReadOnlyList Commands { get; } public CommandError? Error { get; } public string ErrorReason { get; } public bool IsSuccess => !Error.HasValue; - private SearchResult(string text, IReadOnlyList commands, CommandError? error, string errorReason) + private SearchResult(string text, IReadOnlyList commands, CommandError? error, string errorReason) { Text = text; Commands = commands; @@ -22,7 +22,7 @@ namespace Discord.Commands ErrorReason = errorReason; } - public static SearchResult FromSuccess(string text, IReadOnlyList commands) + public static SearchResult FromSuccess(string text, IReadOnlyList commands) => new SearchResult(text, commands, null, null); public static SearchResult FromError(CommandError error, string reason) => new SearchResult(null, null, error, reason); diff --git a/src/Discord.Net.Core/Utils/ConcurrentHashSet.cs b/src/Discord.Net.Core/Utils/ConcurrentHashSet.cs index 3a7b39643..1ef105527 100644 --- a/src/Discord.Net.Core/Utils/ConcurrentHashSet.cs +++ b/src/Discord.Net.Core/Utils/ConcurrentHashSet.cs @@ -9,7 +9,7 @@ namespace Discord { //Based on https://github.com/dotnet/corefx/blob/d0dc5fc099946adc1035b34a8b1f6042eddb0c75/src/System.Threading.Tasks.Parallel/src/System/Threading/PlatformHelper.cs //Copyright (c) .NET Foundation and Contributors - public static class ConcurrentHashSet + internal static class ConcurrentHashSet { private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; private static volatile int s_processorCount;