fix: Cleans up core code (#1187)
**Only one functional change** * Fixes a bug in LogFactory where `Warning` is being logged as `Information` Non-functional changes: * Updates/Fixes copyright headers * Removes namespace scopes for core files. View with [whitespace off](https://github.com/modernuo/ModernUO/pull/1187/files?w=1).
This commit is contained in:
parent
0138d40bda
commit
f268d5d4e2
262 changed files with 28527 additions and 28646 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: AssemblyHandler.cs *
|
||||
* *
|
||||
|
|
@ -21,334 +21,333 @@ using System.Reflection;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public static class AssemblyHandler
|
||||
{
|
||||
public static class AssemblyHandler
|
||||
private static readonly Dictionary<Assembly, TypeCache> m_TypeCaches = new();
|
||||
private static TypeCache m_NullCache;
|
||||
public static Assembly[] Assemblies { get; set; }
|
||||
|
||||
internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
|
||||
{
|
||||
private static readonly Dictionary<Assembly, TypeCache> m_TypeCaches = new();
|
||||
private static TypeCache m_NullCache;
|
||||
public static Assembly[] Assemblies { get; set; }
|
||||
|
||||
internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
|
||||
var assemblyName = new AssemblyName(args.Name);
|
||||
var assembly = LoadAssemblyByAssemblyName(assemblyName);
|
||||
if (assembly == null)
|
||||
{
|
||||
var assemblyName = new AssemblyName(args.Name);
|
||||
var assembly = LoadAssemblyByAssemblyName(assemblyName);
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
$"Could not load file or assembly {assemblyName}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}",
|
||||
$"{assemblyName.Name}.dll"
|
||||
);
|
||||
}
|
||||
|
||||
return assembly;
|
||||
throw new FileNotFoundException(
|
||||
$"Could not load file or assembly {assemblyName}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}",
|
||||
$"{assemblyName.Name}.dll"
|
||||
);
|
||||
}
|
||||
|
||||
private static void EnsureAssemblyDirectories()
|
||||
return assembly;
|
||||
}
|
||||
|
||||
private static void EnsureAssemblyDirectories()
|
||||
{
|
||||
if (ServerConfiguration.AssemblyDirectories.Count == 0)
|
||||
{
|
||||
if (ServerConfiguration.AssemblyDirectories.Count == 0)
|
||||
{
|
||||
ServerConfiguration.AssemblyDirectories.Add("./Assemblies");
|
||||
ServerConfiguration.Save();
|
||||
}
|
||||
ServerConfiguration.AssemblyDirectories.Add("./Assemblies");
|
||||
ServerConfiguration.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public static Assembly LoadAssemblyByAssemblyName(AssemblyName assemblyName)
|
||||
public static Assembly LoadAssemblyByAssemblyName(AssemblyName assemblyName)
|
||||
{
|
||||
if (assemblyName?.Name == null)
|
||||
{
|
||||
if (assemblyName?.Name == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fullName = assemblyName.FullName;
|
||||
var fileName = $"{assemblyName.Name}.dll";
|
||||
|
||||
EnsureAssemblyDirectories();
|
||||
var assemblyDirectories = ServerConfiguration.AssemblyDirectories;
|
||||
|
||||
foreach (var assemblyDir in assemblyDirectories)
|
||||
{
|
||||
var assemblyPath = PathUtility.GetFullPath(Path.Combine(assemblyDir, fileName), Core.BaseDirectory);
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
var assemblyNameCheck = AssemblyName.GetAssemblyName(assemblyPath);
|
||||
if (assemblyNameCheck.FullName == fullName)
|
||||
{
|
||||
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Assembly LoadAssemblyByFileName(string assemblyFile)
|
||||
{
|
||||
EnsureAssemblyDirectories();
|
||||
var assemblyDirectories = ServerConfiguration.AssemblyDirectories;
|
||||
var fullName = assemblyName.FullName;
|
||||
var fileName = $"{assemblyName.Name}.dll";
|
||||
|
||||
foreach (var assemblyDir in assemblyDirectories)
|
||||
EnsureAssemblyDirectories();
|
||||
var assemblyDirectories = ServerConfiguration.AssemblyDirectories;
|
||||
|
||||
foreach (var assemblyDir in assemblyDirectories)
|
||||
{
|
||||
var assemblyPath = PathUtility.GetFullPath(Path.Combine(assemblyDir, fileName), Core.BaseDirectory);
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
var assemblyPath = Path.Combine(assemblyDir, assemblyFile);
|
||||
if (File.Exists(assemblyPath))
|
||||
var assemblyNameCheck = AssemblyName.GetAssemblyName(assemblyPath);
|
||||
if (assemblyNameCheck.FullName == fullName)
|
||||
{
|
||||
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Assembly LoadAssemblyByFileName(string assemblyFile)
|
||||
{
|
||||
EnsureAssemblyDirectories();
|
||||
var assemblyDirectories = ServerConfiguration.AssemblyDirectories;
|
||||
|
||||
foreach (var assemblyDir in assemblyDirectories)
|
||||
{
|
||||
var assemblyPath = Path.Combine(assemblyDir, assemblyFile);
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void LoadAssemblies(string[] files)
|
||||
{
|
||||
var assemblies = new Assembly[files.Length];
|
||||
|
||||
for (var i = 0; i < files.Length; i++)
|
||||
{
|
||||
var assemblyFile = files[i];
|
||||
var assembly = LoadAssemblyByFileName(assemblyFile);
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
$"Could not load file or assembly {assemblyFile}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}",
|
||||
assemblyFile
|
||||
);
|
||||
}
|
||||
|
||||
assemblies[i] = assembly;
|
||||
}
|
||||
|
||||
Assemblies = assemblies;
|
||||
}
|
||||
|
||||
public static void Invoke(string method)
|
||||
{
|
||||
var invoke = new List<MethodInfo>();
|
||||
|
||||
Core.Assembly.AddMethods(method, invoke);
|
||||
|
||||
for (var i = 0; i < Assemblies.Length; i++)
|
||||
{
|
||||
Assemblies[i].AddMethods(method, invoke);
|
||||
}
|
||||
|
||||
invoke.Sort(new CallPriorityComparer());
|
||||
|
||||
for (var i = 0; i < invoke.Count; ++i)
|
||||
{
|
||||
invoke[i].Invoke(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddMethods(this Assembly assembly, string method, List<MethodInfo> list)
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
var m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public);
|
||||
if (m?.GetParameters().Length == 0)
|
||||
{
|
||||
list.Add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static TypeCache GetTypeCache(Assembly asm)
|
||||
{
|
||||
if (asm == null)
|
||||
{
|
||||
return m_NullCache ??= new TypeCache(null);
|
||||
}
|
||||
|
||||
if (m_TypeCaches.TryGetValue(asm, out var c))
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
return m_TypeCaches[asm] = new TypeCache(asm);
|
||||
}
|
||||
|
||||
public static Type FindTypeByFullName(string name, bool ignoreCase = true) =>
|
||||
FindTypeByName(name, true, ignoreCase);
|
||||
|
||||
public static Type FindTypeByName(string name, bool fullName = false, bool ignoreCase = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void LoadAssemblies(string[] files)
|
||||
for (var i = 0; i < Assemblies.Length; i++)
|
||||
{
|
||||
var assemblies = new Assembly[files.Length];
|
||||
|
||||
for (var i = 0; i < files.Length; i++)
|
||||
{
|
||||
var assemblyFile = files[i];
|
||||
var assembly = LoadAssemblyByFileName(assemblyFile);
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
$"Could not load file or assembly {assemblyFile}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}",
|
||||
assemblyFile
|
||||
);
|
||||
}
|
||||
|
||||
assemblies[i] = assembly;
|
||||
}
|
||||
|
||||
Assemblies = assemblies;
|
||||
}
|
||||
|
||||
public static void Invoke(string method)
|
||||
{
|
||||
var invoke = new List<MethodInfo>();
|
||||
|
||||
Core.Assembly.AddMethods(method, invoke);
|
||||
|
||||
for (var i = 0; i < Assemblies.Length; i++)
|
||||
{
|
||||
Assemblies[i].AddMethods(method, invoke);
|
||||
}
|
||||
|
||||
invoke.Sort(new CallPriorityComparer());
|
||||
|
||||
for (var i = 0; i < invoke.Count; ++i)
|
||||
{
|
||||
invoke[i].Invoke(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddMethods(this Assembly assembly, string method, List<MethodInfo> list)
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
var m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public);
|
||||
if (m?.GetParameters().Length == 0)
|
||||
{
|
||||
list.Add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static TypeCache GetTypeCache(Assembly asm)
|
||||
{
|
||||
if (asm == null)
|
||||
{
|
||||
return m_NullCache ??= new TypeCache(null);
|
||||
}
|
||||
|
||||
if (m_TypeCaches.TryGetValue(asm, out var c))
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
return m_TypeCaches[asm] = new TypeCache(asm);
|
||||
}
|
||||
|
||||
public static Type FindTypeByFullName(string name, bool ignoreCase = true) =>
|
||||
FindTypeByName(name, true, ignoreCase);
|
||||
|
||||
public static Type FindTypeByName(string name, bool fullName = false, bool ignoreCase = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Assemblies.Length; i++)
|
||||
{
|
||||
foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, fullName, ignoreCase))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, fullName, ignoreCase))
|
||||
foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, fullName, ignoreCase))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, fullName, ignoreCase))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class TypeCache
|
||||
{
|
||||
private readonly Dictionary<string, int[]> _nameMap = new();
|
||||
private readonly Dictionary<string, int[]> _nameMapInsensitive = new();
|
||||
private readonly Dictionary<string, int[]> _fullNameMap = new();
|
||||
private readonly Dictionary<string, int[]> _fullNameMapInsensitive = new();
|
||||
|
||||
public TypeCache(Assembly asm)
|
||||
{
|
||||
Types = asm?.GetTypes() ?? Type.EmptyTypes;
|
||||
|
||||
var nameMap = new Dictionary<string, HashSet<int>>();
|
||||
var nameMapInsensitive = new Dictionary<string, HashSet<int>>();
|
||||
var fullNameMap = new Dictionary<string, HashSet<int>>();
|
||||
var fullNameMapInsensitive = new Dictionary<string, HashSet<int>>();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void addTypeToRefs(int index, string fullTypeName)
|
||||
{
|
||||
var typeName = fullTypeName[(fullTypeName.LastIndexOf('.') + 1)..];
|
||||
AddToRefs(index, typeName, nameMap);
|
||||
AddToRefs(index, typeName.ToLower(), nameMapInsensitive);
|
||||
AddToRefs(index, fullTypeName, fullNameMap);
|
||||
AddToRefs(index, fullTypeName.ToLower(), fullNameMapInsensitive);
|
||||
}
|
||||
|
||||
var aliasType = typeof(TypeAliasAttribute);
|
||||
for (var i = 0; i < Types.Length; i++)
|
||||
{
|
||||
var current = Types[i];
|
||||
addTypeToRefs(i, current.FullName);
|
||||
if (current.GetCustomAttribute(aliasType, false) is TypeAliasAttribute alias)
|
||||
{
|
||||
for (var j = 0; j < alias.Aliases.Length; j++)
|
||||
{
|
||||
addTypeToRefs(i, alias.Aliases[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (key, value) in nameMap)
|
||||
{
|
||||
_nameMap[key] = value.ToArray();
|
||||
}
|
||||
|
||||
foreach (var (key, value) in nameMapInsensitive)
|
||||
{
|
||||
_nameMapInsensitive[key] = value.ToArray();
|
||||
}
|
||||
|
||||
foreach (var (key, value) in fullNameMap)
|
||||
{
|
||||
_fullNameMap[key] = value.ToArray();
|
||||
}
|
||||
|
||||
foreach (var (key, value) in fullNameMapInsensitive)
|
||||
{
|
||||
_fullNameMapInsensitive[key] = value.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class TypeCache
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void AddToRefs(int index, string key, Dictionary<string, HashSet<int>> map)
|
||||
{
|
||||
private readonly Dictionary<string, int[]> _nameMap = new();
|
||||
private readonly Dictionary<string, int[]> _nameMapInsensitive = new();
|
||||
private readonly Dictionary<string, int[]> _fullNameMap = new();
|
||||
private readonly Dictionary<string, int[]> _fullNameMapInsensitive = new();
|
||||
|
||||
public TypeCache(Assembly asm)
|
||||
if (key == null)
|
||||
{
|
||||
Types = asm?.GetTypes() ?? Type.EmptyTypes;
|
||||
return;
|
||||
}
|
||||
|
||||
var nameMap = new Dictionary<string, HashSet<int>>();
|
||||
var nameMapInsensitive = new Dictionary<string, HashSet<int>>();
|
||||
var fullNameMap = new Dictionary<string, HashSet<int>>();
|
||||
var fullNameMapInsensitive = new Dictionary<string, HashSet<int>>();
|
||||
if (map.TryGetValue(key, out var refs))
|
||||
{
|
||||
refs.Add(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
refs = new HashSet<int> { index };
|
||||
map.Add(key, refs);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void addTypeToRefs(int index, string fullTypeName)
|
||||
{
|
||||
var typeName = fullTypeName[(fullTypeName.LastIndexOf('.') + 1)..];
|
||||
AddToRefs(index, typeName, nameMap);
|
||||
AddToRefs(index, typeName.ToLower(), nameMapInsensitive);
|
||||
AddToRefs(index, fullTypeName, fullNameMap);
|
||||
AddToRefs(index, fullTypeName.ToLower(), fullNameMapInsensitive);
|
||||
}
|
||||
public Type[] Types { get; }
|
||||
|
||||
var aliasType = typeof(TypeAliasAttribute);
|
||||
for (var i = 0; i < Types.Length; i++)
|
||||
{
|
||||
var current = Types[i];
|
||||
addTypeToRefs(i, current.FullName);
|
||||
if (current.GetCustomAttribute(aliasType, false) is TypeAliasAttribute alias)
|
||||
{
|
||||
for (var j = 0; j < alias.Aliases.Length; j++)
|
||||
{
|
||||
addTypeToRefs(i, alias.Aliases[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TypeEnumerable GetTypesByName(string name, bool full, bool ignoreCase) => new(name, this, full, ignoreCase);
|
||||
|
||||
foreach (var (key, value) in nameMap)
|
||||
{
|
||||
_nameMap[key] = value.ToArray();
|
||||
}
|
||||
public ref struct TypeEnumerable
|
||||
{
|
||||
private readonly TypeCache _cache;
|
||||
private readonly string _name;
|
||||
private readonly bool _ignoreCase;
|
||||
private readonly bool _full;
|
||||
|
||||
foreach (var (key, value) in nameMapInsensitive)
|
||||
{
|
||||
_nameMapInsensitive[key] = value.ToArray();
|
||||
}
|
||||
|
||||
foreach (var (key, value) in fullNameMap)
|
||||
{
|
||||
_fullNameMap[key] = value.ToArray();
|
||||
}
|
||||
|
||||
foreach (var (key, value) in fullNameMapInsensitive)
|
||||
{
|
||||
_fullNameMapInsensitive[key] = value.ToArray();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TypeEnumerable(string name, TypeCache cache, bool full, bool ignoreCase)
|
||||
{
|
||||
_name = name;
|
||||
_cache = cache;
|
||||
_ignoreCase = ignoreCase;
|
||||
_full = full;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void AddToRefs(int index, string key, Dictionary<string, HashSet<int>> map)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
public TypeEnumerator GetEnumerator() => new(_name, _cache, _full, _ignoreCase);
|
||||
}
|
||||
|
||||
if (map.TryGetValue(key, out var refs))
|
||||
public ref struct TypeEnumerator
|
||||
{
|
||||
private readonly TypeCache _cache;
|
||||
private readonly int[] _values;
|
||||
private int _index;
|
||||
private Type _current;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal TypeEnumerator(string name, TypeCache cache, bool full, bool ignoreCase)
|
||||
{
|
||||
_cache = cache;
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
refs.Add(index);
|
||||
var map = full ? _cache._fullNameMapInsensitive : _cache._nameMapInsensitive;
|
||||
_values = map.TryGetValue(name.ToLower(), out var values) ? values : Array.Empty<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
refs = new HashSet<int> { index };
|
||||
map.Add(key, refs);
|
||||
var map = full ? _cache._fullNameMap : _cache._nameMap;
|
||||
_values = map.TryGetValue(name, out var values) ? values : Array.Empty<int>();
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types { get; }
|
||||
_index = 0;
|
||||
_current = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TypeEnumerable GetTypesByName(string name, bool full, bool ignoreCase) => new(name, this, full, ignoreCase);
|
||||
|
||||
public ref struct TypeEnumerable
|
||||
public bool MoveNext()
|
||||
{
|
||||
private readonly TypeCache _cache;
|
||||
private readonly string _name;
|
||||
private readonly bool _ignoreCase;
|
||||
private readonly bool _full;
|
||||
int[] localList = _values;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TypeEnumerable(string name, TypeCache cache, bool full, bool ignoreCase)
|
||||
if ((uint)_index < (uint)localList.Length)
|
||||
{
|
||||
_name = name;
|
||||
_cache = cache;
|
||||
_ignoreCase = ignoreCase;
|
||||
_full = full;
|
||||
_current = _cache.Types[_values[_index++]];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TypeEnumerator GetEnumerator() => new(_name, _cache, _full, _ignoreCase);
|
||||
return false;
|
||||
}
|
||||
|
||||
public ref struct TypeEnumerator
|
||||
public Type Current
|
||||
{
|
||||
private readonly TypeCache _cache;
|
||||
private readonly int[] _values;
|
||||
private int _index;
|
||||
private Type _current;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal TypeEnumerator(string name, TypeCache cache, bool full, bool ignoreCase)
|
||||
{
|
||||
_cache = cache;
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
var map = full ? _cache._fullNameMapInsensitive : _cache._nameMapInsensitive;
|
||||
_values = map.TryGetValue(name.ToLower(), out var values) ? values : Array.Empty<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = full ? _cache._fullNameMap : _cache._nameMap;
|
||||
_values = map.TryGetValue(name, out var values) ? values : Array.Empty<int>();
|
||||
}
|
||||
|
||||
_index = 0;
|
||||
_current = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
int[] localList = _values;
|
||||
|
||||
if ((uint)_index < (uint)localList.Length)
|
||||
{
|
||||
_current = _cache.Types[_values[_index++]];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _current;
|
||||
}
|
||||
get => _current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue