From 1a1b2025cd1526a490910940a8a64916bea4b23a Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 28 Nov 2022 00:43:50 +0100 Subject: [PATCH] Made JsonTypeSafeWrapper.Of generic to potentially avoid reflective instantiation --- CHANGELOG.md | 1 + .../Json/JsonTypeSafeGenericDataHolder.cs | 3 +-- MLEM.Data/Json/JsonTypeSafeWrapper.cs | 18 +++++++++++------- Tests/DataTests.cs | 8 +++++++- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb6c257..6662225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ Improvements - Added trimming and AOT annotations and made MLEM.Data trimmable - Store a RuntimeTexturePacker packed texture region's source region - Use JSON.NET attributes in favor of DataContract and DataMember +- Made JsonTypeSafeWrapper.Of generic to potentially avoid reflective instantiation Fixes - Fixed data texture atlases not allowing most characters in their region names diff --git a/MLEM.Data/Json/JsonTypeSafeGenericDataHolder.cs b/MLEM.Data/Json/JsonTypeSafeGenericDataHolder.cs index 496e44c..ffea51a 100644 --- a/MLEM.Data/Json/JsonTypeSafeGenericDataHolder.cs +++ b/MLEM.Data/Json/JsonTypeSafeGenericDataHolder.cs @@ -33,8 +33,7 @@ namespace MLEM.Data.Json { } else { if (this.data == null) this.data = new Dictionary(); - // if types already match exactly, we don't need to use Of (which requires dynamic code) - this.data[key] = data.GetType() == typeof(T) ? new JsonTypeSafeWrapper(data) : JsonTypeSafeWrapper.Of(data); + this.data[key] = JsonTypeSafeWrapper.Of(data); } } diff --git a/MLEM.Data/Json/JsonTypeSafeWrapper.cs b/MLEM.Data/Json/JsonTypeSafeWrapper.cs index 731c1c0..cb06154 100644 --- a/MLEM.Data/Json/JsonTypeSafeWrapper.cs +++ b/MLEM.Data/Json/JsonTypeSafeWrapper.cs @@ -29,17 +29,21 @@ namespace MLEM.Data.Json { /// /// Creates a new from the given value. - /// The type parameter of the returned wrapper will be equal to the of the passed. - /// If a for a specific type, known at comepile type, should be created, you can use . + /// The type parameter of the returned wrapper will be equal to the of the passed, even if it is a subtype of . + /// If a for a specific type, known at compile type, should be created, you can use . /// /// The value to wrap /// A with a type matching the type of #if NET7_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The native code for this instantiation might not be available at runtime if the value's type is a subtype of T.")] #endif - public static JsonTypeSafeWrapper Of(object value) { - var type = typeof(JsonTypeSafeWrapper<>).MakeGenericType(value.GetType()); - return (JsonTypeSafeWrapper) Activator.CreateInstance(type, value); + public static JsonTypeSafeWrapper Of(T value) { + if (value.GetType() == typeof(T)) { + return new JsonTypeSafeWrapper(value); + } else { + var type = typeof(JsonTypeSafeWrapper<>).MakeGenericType(value.GetType()); + return (JsonTypeSafeWrapper) Activator.CreateInstance(type, value); + } } } @@ -55,7 +59,7 @@ namespace MLEM.Data.Json { /// /// Creates a new json type-safe wrapper instance that wraps the given . - /// If the type of the value is unknown at compile time, can be used instead. + /// If the type of the value is unknown at compile time, can be used instead. /// /// The value to wrap public JsonTypeSafeWrapper(T value) { diff --git a/Tests/DataTests.cs b/Tests/DataTests.cs index 42d9aaa..6fadd7c 100644 --- a/Tests/DataTests.cs +++ b/Tests/DataTests.cs @@ -40,9 +40,15 @@ public class DataTests { var safeData = new JsonTypeSafeGenericDataHolder(); // data holder should wrap the time span to ensure that it stays a time span safeData.SetData("Time", TimeSpan.FromMinutes(5)); + // also check path that creates an instance through reflection + safeData.SetData("Time2", TimeSpan.FromMinutes(15)); + var safeRead = DataTests.SerializeAndDeserialize(safeData); Assert.IsInstanceOf(safeRead.GetData("Time")); - Assert.DoesNotThrow(() => safeRead.GetData("Time")); + Assert.AreEqual(TimeSpan.FromMinutes(5), safeRead.GetData("Time")); + + Assert.IsInstanceOf(safeRead.GetData("Time2")); + Assert.AreEqual(TimeSpan.FromMinutes(15), safeRead.GetData("Time2")); } private static T SerializeAndDeserialize(T t) {