Lots of new optimizations utilizing prepatcher

To start, this mod now requires Prepatcher and Fishery.
New optimizations include: gene ticking improvements, world object improvements, hediff optimizations, world pawn gc optimizations, better mothballing, dds support, ContentFinder caching, additions and changes to reflection caching, as well as various other additions. Multiplayer support has been improved too, but should be considered highly experimental. The WorkGiver_DoBill bug is fixed in this build. Multiple patches aiming to improve RimWorld's load time have been added too.
This commit is contained in:
bbradson
2023-06-03 20:24:38 +02:00
parent cb0a407425
commit ca9321add8
125 changed files with 11272 additions and 3681 deletions
@@ -0,0 +1,302 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using Mono.Collections.Generic;
namespace PerformanceFish.Utility;
public static class CollectionExtensions
{
public static bool Any<T>(this T[] array, Predicate<T> predicate)
{
var length = array.Length;
return length != 0 && Any(ref array[0], predicate, (nuint)length);
}
// overlaps with method in GenCollection
// public static bool Any<T>(this List<T> list, Predicate<T> predicate)
// {
// var count = list.Count;
// return count != 0 && Any(ref list._items[0], predicate, count);
// }
public static bool Any<T>(this IList<T> iList, Predicate<T> predicate)
{
var count = iList.Count;
for (var i = 0; i < count; i++)
{
if (predicate(iList[i]))
return true;
}
return false;
}
public static unsafe bool Any<T>(this T[] array, delegate*<T, bool> predicate)
{
var length = array.Length;
return length != 0 && Any(ref array[0], predicate, (nuint)length);
}
public static unsafe bool Any<T>(this List<T> list, delegate*<T, bool> predicate)
{
var count = list.Count;
return count != 0 && Any(ref list._items[0], predicate, (nuint)count);
}
public static bool ExistsAndNotNull<T>(this List<T>? list, Predicate<T> match)
{
if (list is null)
return false;
var count = list.Count;
return count != 0 && Any(ref list._items[0], match, (nuint)count);
}
public static bool Contains<T>(this T[] array, T item)
{
var length = array.Length;
return length != 0 && Contains(ref array[0], item, (nuint)length);
}
public static bool Contains<T>(this IList<T> iList, T item)
{
var count = iList.Count;
for (var i = 0; i < count; i++)
{
if (iList[i].Equals<T>(item))
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? FirstOrDefault<T>(this T[] array, Predicate<T> predicate)
{
var length = array.Length;
return length == 0 ? default : FirstOrDefault(ref array[0], predicate, (nuint)length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? FirstOrDefaultFast<T>(this List<T> list, Predicate<T> predicate)
{
var count = list.Count;
return count == 0 ? default : FirstOrDefault(ref list._items[0], predicate, (nuint)count);
}
public static T? FirstOrDefault<T>(this IList<T> iList, Predicate<T> predicate)
{
var count = iList.Count;
for (var i = 0; i < count; i++)
{
if (predicate(iList[i]))
return iList[i];
}
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T? FirstOrDefault<T>(this T[] array, delegate*<T, bool> predicate)
{
var length = array.Length;
return length == 0 ? default : FirstOrDefault(ref array[0], predicate, (nuint)length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T? FirstOrDefault<T>(this List<T> list, delegate*<T, bool> predicate)
{
var count = list.Count;
return count == 0 ? default : FirstOrDefault(ref list._items[0], predicate, (nuint)count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? FirstOrDefault<T>(this T[] array) => array.Length != 0 ? array[0] : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? FirstOrDefault<T>(this List<T> list) => list.Count != 0 ? list[0] : default;
public static T? FirstOrDefault<T>(this IList<T> iList) => iList.Count != 0 ? iList[0] : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T First<T>(this T[] array, Predicate<T> predicate)
{
var length = array.Length;
return length == 0 ? ThrowForNoElements<T>() : First(ref array[0], predicate, (nuint)length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T First<T>(this List<T> list, Predicate<T> predicate)
{
var count = list.Count;
return count == 0 ? ThrowForNoElements<T>() : First(ref list._items[0], predicate, (nuint)count);
}
public static T First<T>(this IList<T> iList, Predicate<T> predicate)
{
var count = iList.Count;
for (var i = 0; i < count; i++)
{
if (predicate(iList[i]))
return iList[i];
}
return ThrowForNoElements<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T First<T>(this T[] array, delegate*<T, bool> predicate)
{
var length = array.Length;
return length == 0 ? ThrowForNoElements<T>() : First(ref array[0], predicate, (nuint)length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T First<T>(this List<T> list, delegate*<T, bool> predicate)
{
var count = list.Count;
return count == 0 ? ThrowForNoElements<T>() : First(ref list._items[0], predicate, (nuint)count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T First<T>(this T[] array) => array.Length != 0 ? array[0] : ThrowForNoElements<T>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T First<T>(this List<T> list) => list.Count != 0 ? list[0] : ThrowForNoElements<T>();
public static T First<T>(this IList<T> iList) => iList.Count != 0 ? iList[0] : ThrowForNoElements<T>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe bool Any<T>(ref T reference, Predicate<T> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return true;
i += (nuint)sizeof(T);
}
while (i < length);
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe bool Any<T>(ref T reference, delegate*<T, bool> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return true;
i += (nuint)sizeof(T);
}
while (i < length);
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe bool Contains<T>(ref T reference, T item, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (Unsafe.AddByteOffset(ref reference, i).Equals<T>(item))
return true;
i += (nuint)sizeof(T);
}
while (i < length);
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe T? FirstOrDefault<T>(ref T reference, Predicate<T> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return Unsafe.AddByteOffset(ref reference, i);
i += (nuint)sizeof(T);
}
while (i < length);
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe T First<T>(ref T reference, Predicate<T> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return Unsafe.AddByteOffset(ref reference, i);
i += (nuint)sizeof(T);
}
while (i < length);
return ThrowForNoElements<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe T? FirstOrDefault<T>(ref T reference, delegate*<T, bool> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return Unsafe.AddByteOffset(ref reference, i);
i += (nuint)sizeof(T);
}
while (i < length);
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe T First<T>(ref T reference, delegate*<T, bool> predicate, nuint length)
{
nuint i = 0;
length *= (nuint)sizeof(T);
do
{
if (predicate(Unsafe.AddByteOffset(ref reference, i)))
return Unsafe.AddByteOffset(ref reference, i);
i += (nuint)sizeof(T);
}
while (i < length);
return ThrowForNoElements<T>();
}
[DoesNotReturn]
private static T ThrowForNoElements<T>()
=> throw new InvalidOperationException("Sequence contains no elements");
}
+11 -10
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 bradson
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
@@ -6,12 +6,14 @@
namespace PerformanceFish.Utility;
/// <summary>
/// Scribe_Deep normally creates a new instance when loading data. Scribe on this class prevents that. Also includes a try-catch block
/// Scribe_Deep normally creates a new instance when loading data. Scribe on this class prevents that. Also includes a
/// try-catch block
/// </summary>
public class Exposable : IExposable
{
public IExposable Parent;
public readonly IExposable Parent;
public Exposable(IExposable @this) => Parent = @this;
public void ExposeData()
{
try
@@ -23,22 +25,21 @@ public class Exposable : IExposable
Log.Error($"{ex}");
}
}
public override string ToString() => TrimName(Parent.ToString());
public override bool Equals(object obj) => obj.Equals(Parent);
public override int GetHashCode() => Parent.GetHashCode();
public static void Scribe(IExposable iexposable, string label, bool trimLabel = true)
public static void Scribe(IExposable iExposable, string label, bool trimLabel = true)
{
var wrappedExposable = new Exposable(iexposable);
Scribe_Deep.Look(ref wrappedExposable,
trimLabel ? TrimName(label) : label,
new[] { iexposable });
var wrappedExposable = new Exposable(iExposable);
Scribe_Deep.Look(ref wrappedExposable, trimLabel ? TrimName(label) : label, iExposable);
}
internal static string TrimName(string name)
=> name.Replace('+', '.') is var fullName
&& $"{typeof(PerformanceFishMod).Namespace}." is var @namespace
&& fullName.StartsWith(@namespace)
? fullName.Remove(0, @namespace.Length)
: fullName;
? fullName.Remove(0, @namespace.Length)
: fullName;
}
+70 -43
View File
@@ -1,17 +1,27 @@
// Copyright (c) 2022 bradson
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace PerformanceFish.Utility;
[PublicAPI]
public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>
{
public IList<TKey> Keys { get; }
public IList<TValue> Values { get; }
public IList<TKey> Keys
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public IList<TValue> Values
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public KeyedList()
{
@@ -33,16 +43,16 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
public TValue this[TKey key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Guard.IsNotNull(key);
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
var (itemKey, itemValue) = items[i];
if (GetKeyComparer().Equals(itemKey, key))
return itemValue;
if (items[i].Key.Equals<TKey>(key))
return items[i].Value;
}
return ThrowHelperF.ThrowKeyNotFoundException<TKey, TValue>(key);
@@ -52,14 +62,14 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
Guard.IsNotNull(key);
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
ref var item = ref items[i];
if (GetKeyComparer().Equals(item.Key, key))
{
item = new(item.Key, value);
return;
}
if (!item.Key.Equals<TKey>(key))
continue;
item = new(item.Key, value);
return;
}
Add(new(key, value));
@@ -68,29 +78,31 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
public void Add(TKey key, TValue value) => Add(new(key, value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(TKey item)
{
Guard.IsNotNull(item);
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
if (GetKeyComparer().Equals(items[i].Key, item))
if (items[i].Key.Equals<TKey>(item))
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsValue(TValue item)
{
if (item is null)
return false;
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
if (GetValueComparer().Equals(items[i].Value, item))
if (items[i].Value.Equals<TValue>(item))
return true;
}
@@ -98,20 +110,24 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
}
#pragma warning disable CS8767 // missing nullable on IDictionary interface
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out TValue? value)
#pragma warning restore CS8767
{
Guard.IsNotNull(key);
var index = IndexOfKey(key);
if (index < 0)
var items = _items;
for (var i = Count - 1; i >= 0; i--)
{
value = default;
return false;
if (!items[i].Key.Equals<TKey>(key))
continue;
value = items[i].Value;
return true;
}
value = _items[index].Value;
return true;
value = default;
return false;
}
public void CopyTo(TKey[] array, int arrayIndex)
@@ -122,6 +138,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
for (var i = 0; i < Count; i++)
array[i + arrayIndex] = items[i].Key;
}
public void CopyTo(TValue[] array, int arrayIndex)
{
CanCopyTo(array, arrayIndex);
@@ -160,26 +177,28 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOfKey(TKey key)
{
Guard.IsNotNull(key);
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
if (GetKeyComparer().Equals(items[i].Key, key))
if (items[i].Key.Equals<TKey>(key))
return i;
}
return -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOfValue(TValue value)
{
var items = _items;
for (var i = 0; i < Count; i++)
for (var i = Count - 1; i >= 0; i--)
{
if (GetValueComparer().Equals(items[i].Value, value))
if (items[i].Value.Equals<TValue>(value))
return i;
}
@@ -190,16 +209,21 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
{
private readonly KeyedList<TKey, TValue> _list;
public KeyCollection(KeyedList<TKey, TValue> list)
=> _list = list;
public KeyCollection(KeyedList<TKey, TValue> list) => _list = list;
public TKey this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _list[index].Key;
// ReSharper disable once ValueParameterNotUsed
set => ThrowHelper.ThrowNotSupportedException();
}
public int Count => _list.Count;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _list.Count;
}
public bool IsReadOnly => false;
@@ -207,6 +231,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
public void Clear() => _list.Clear();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(TKey item) => _list.ContainsKey(item);
public void CopyTo(TKey[] array, int arrayIndex)
@@ -218,6 +243,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
array[i + arrayIndex] = items[i].Key;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator<TKey> GetEnumerator()
{
var items = _list._items;
@@ -225,6 +251,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
yield return items[i].Key;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(TKey item) => _list.IndexOfKey(item);
public void Insert(int index, TKey item) => ThrowHelper.ThrowNotSupportedException();
@@ -240,11 +267,11 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
{
private readonly KeyedList<TKey, TValue> _list;
public ValueCollection(KeyedList<TKey, TValue> list)
=> _list = list;
public ValueCollection(KeyedList<TKey, TValue> list) => _list = list;
public TValue this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _list[index].Value;
set
{
@@ -253,7 +280,11 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
}
}
public int Count => _list.Count;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _list.Count;
}
public bool IsReadOnly => false;
@@ -261,6 +292,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
public void Clear() => _list.Clear();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(TValue item) => _list.ContainsValue(item);
public void CopyTo(TValue[] array, int arrayIndex)
@@ -272,6 +304,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
array[i + arrayIndex] = items[i].Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator<TValue> GetEnumerator()
{
var items = _list._items;
@@ -279,6 +312,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
yield return items[i].Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(TValue item) => _list.IndexOfValue(item);
public void Insert(int index, TValue item) => ThrowHelper.ThrowNotSupportedException();
@@ -290,12 +324,6 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static IEqualityComparer<TKey> GetKeyComparer() => EqualityComparer<TKey>.Default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static IEqualityComparer<TValue> GetValueComparer() => EqualityComparer<TValue>.Default;
ICollection<TKey> IDictionary<TKey, TValue>.Keys => Keys;
ICollection<TValue> IDictionary<TKey, TValue>.Values => Values;
@@ -307,8 +335,7 @@ public class KeyedList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IDictio
protected static class ThrowHelperF
{
[DoesNotReturn]
internal static V ThrowKeyNotFoundException<T, V>(T key)
=> throw GetKeyNotFoundException(key);
internal static V ThrowKeyNotFoundException<T, V>(T key) => throw GetKeyNotFoundException(key);
[DoesNotReturn]
internal static void ThrowKeyNotFoundException<T>(T key)
@@ -0,0 +1,98 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using JetBrains.Annotations;
namespace PerformanceFish.Utility;
[PublicAPI]
public static class ReflectionExtensions
{
/// <summary>
/// Returns the first method that matches either by name OR by predicate
/// </summary>
public static MethodInfo? TryGetMethod(this Type type, string nameCaseInsensitive,
Predicate<MethodInfo>? predicate = null)
{
predicate ??= static _ => true;
var methods = type.GetMethods(AccessTools.all);
for (var i = 0; i < methods.Length; i++)
{
if (methods[i].NameEqualsCaseInsensitive(nameCaseInsensitive) || predicate(methods[i]))
return methods[i];
}
return null;
}
public static IEnumerable<Type> SubclassesWithNoMethodOverride(this Type type, params string[] names)
{
Guard.IsNotNull(type);
Guard.IsNotNull(names);
Guard.IsNotEmpty(names);
var allSubclasses = type.AllSubclassesNonAbstract();
for (var i = 0; i < allSubclasses.Count; i++)
{
var hasOverrides = false;
for (var j = names.Length; j-- > 0;)
{
if (AccessTools.DeclaredMethod(allSubclasses[i], names[j]) is null)
continue;
hasOverrides = true;
break;
}
if (!hasOverrides)
yield return allSubclasses[i];
}
}
public static IEnumerable<Type> SubclassesWithNoMethodOverrideAndSelf(this Type type, params string[] names)
{
yield return type;
foreach (var subclass in type.SubclassesWithNoMethodOverride(names))
yield return subclass;
}
public static IEnumerable<Type> SubclassesWithNoMethodOverride(this Type type, Type[] allowedDeclaringTypes,
params string[] names)
{
Guard.IsNotNull(type);
Guard.IsNotNull(allowedDeclaringTypes);
Guard.IsNotEmpty(allowedDeclaringTypes);
Guard.IsNotNull(names);
Guard.IsNotEmpty(names);
var allSubclasses = type.AllSubclassesNonAbstract();
for (var i = 0; i < allSubclasses.Count; i++)
{
var hasOverrides = false;
for (var j = names.Length; j-- > 0;)
{
if (allowedDeclaringTypes.Contains(AccessTools.Method(allSubclasses[i], names[j])!.DeclaringType!))
continue;
hasOverrides = true;
break;
}
if (!hasOverrides)
yield return allSubclasses[i];
}
}
public static IEnumerable<Type> SubclassesWithNoMethodOverrideAndSelf(this Type type, Type[] allowedDeclaringTypes,
params string[] names)
{
yield return type;
foreach (var subclass in type.SubclassesWithNoMethodOverride(allowedDeclaringTypes, names))
yield return subclass;
}
}
@@ -0,0 +1,12 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
namespace PerformanceFish.Utility;
public static class StringExtensions
{
public static bool NameEqualsCaseInsensitive(this MethodInfo methodInfo, string name)
=> methodInfo.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase);
}