Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor custom serialization and known type serialization #204

Merged
merged 2 commits into from Sep 2, 2022

Conversation

axunonb
Copy link
Collaborator

@axunonb axunonb commented Aug 18, 2022

Resolves #203

UdtWrapper

Make UdtWrapper.GetFieldsToBeSerialized private, add methods

  • UdtWrapper.GetFieldsForSerialization()
  • UdtWrapper.GetFieldsForDeserialization(bool)
  • Move member-related checks, that are needed for serialization and deserialization from Serialization.SerializeFields(...) and Deserialization.DeserializeDefault(...) to UdtWrapper.GetFieldsToBeSerialized(...)

Custom serialization and known type serialization

Note: Files in folder "Custom" (namespace not yet aligned)
Create namespace YAXLib.Customization (#57aab90)
Included types: CustomSerializerWrapper, IMemberContext, ISerializationContext, ITypeContext, Locker, MemberContext, SerializationContext, TypeContext

  • ISerializationContext / SerializationContext
    • Refactor: Members are ITypeContext, IMemberContext?, SerializerOptions, RecursionCount
    • ISerializationContext.ITypeContext will always be set
    • If a member is de/serialized IMemberContext will always be assigned for ICustomSerializer and IKnownType
  • ITypeContext / TypeContext
    • Has methods to de/serialize the current type
    • Has methods GetFieldsForSerialization() and GetFieldsForDeserialization
  • IMemberContext / MemberContext
    • Has members MemberInfo, FieldInfo?, PropertyInfo?, TypeContext
    • Has method GetValue()
  • CustomSerializerWrapper
    • A wrapper for all ICustomSerializers, with Reflection code formerly contained in removed ReflectionBridgeExtensions
  • Locker, used in Serialization, Deserialization, CustomSerializerWrapper
    • Blocks recursive calls to ICustomSerializers and IKnownTypes
  • ICustomSerializer: Update xmldoc for changing element/attribute name
  • Refactor Serialization.InvokeCustomSerializer: Code equivalent to Deserialization.InvokeCustomSerializer

ExceptionKnownBaseType

  • Refactor using new features of SerializationContext

Other

  • UdtWrapper.Alias is not nullable
  • MemberWrapper.Alias is not nullable
  • XMLUtils.ToXmlValue handles null properly

Unit Tests

  • Add tests for new features
  • Modified existing tests to use new features

**** UdtWrapper ****

Make UdtWrapper.GetFieldsToBeSerialized private, add methods
* UdtWrapper.GetFieldsForSerialization()
* UdtWrapper.GetFieldsForDeserialization(bool)
* Move member-related checks, that are needed for serialization and deserialization from Serialization.SerializeFields(...) and Deserialization.DeserializeDefault(...) to UdtWrapper.GetFieldsToBeSerialized(...)

**** Folder "Custom" (namespace not yet aligned) ****

* ISerializationContext / SerializationContext
  - Refactor:  Members are ITypeContext, IMemberContext?, SerializerOptions, RecursionCount
  - ISerializationContext.ITypeContext will always be set
  - If a member is de/serialized IMemberContext will always be assigned for ICustomSerializer and IKnownType
* ITypeContext / TypeContext
  - Has methods to de/serialize the current type
  - Has methods GetFieldsForSerialization() and GetFieldsForDeserialization
* IMemberContext / MemberContext
  - Has members MemberInfo, FieldInfo?, PropertyInfo?, TypeContext
  - Has method GetValue()
* CustomSerializerWrapper
  - A wrapper for all ICustomSerializers
* Locker, used in Serialization, Deserialization, CustomSerializerWrapper
  - Blocks recursive calls to ICustomSerializers and IKnownTypes
* ICustomSerializer: Update xmldoc for changing element/attribute name
* Refactor Serialization.InvokeCustomSerializer: Code equivalent to Deserialization.InvokeCustomSerializer

**** ExceptionKnownBaseType ****

* Refactor using new features of SerializationContext

**** Invokation of ICustomSerializers ****

* Remove ReflectionBridgeExtensions
* Add CustomSerializerWrapper which includes invokation

**** Other ****

* UdtWrapper.Alias is not nullable
* MemberWrapper.Alias is not nullable
* XMLUtils.ToXmlValue handles null properly

**** Unit tests ****

* Add tests for new features
* Modified existing tests to use new features
@axunonb
Copy link
Collaborator Author

axunonb commented Aug 18, 2022

SonarCloud quality gate
Finally the last big milestone in the Version/v4.0 project.

@@ -34,14 +34,14 @@ public YAXCustomSerializerAttribute(Type customSerializerType)
void IYaxMemberLevelAttribute.Setup(MemberWrapper memberWrapper)
{
EnsureDesiredInterface(memberWrapper.MemberType);
memberWrapper.CustomSerializerType = CustomSerializerType;
memberWrapper.CustomSerializer = new CustomSerializerWrapper(CustomSerializerType);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MemberWrapper stores an instance of the custom serializer. Before, a knew instance was created per invokation of custom serializer.

/// A wrapper around an <see cref="ICustomSerializer{T}"/>.
/// It's methods are invoked with <see cref="System.Reflection"/>.
/// </summary>
internal class CustomSerializerWrapper : ICustomSerializer<object>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a wrapper for the ICustomSerializer instances.
The wrapper includes Reflection code to invoke ICustomSerializer methods.

@@ -0,0 +1,43 @@
// Copyright (C) Sina Iravanian, Julian Verdurmen, axuno gGmbH and other contributors.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a member of ISerializationContext, and provides all member-specific information.
Before, Type and Member-specific information were included side-by-side.

@@ -0,0 +1,58 @@
// Copyright (C) Sina Iravanian, Julian Verdurmen, axuno gGmbH and other contributors.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provides all Type-specific information.
Newly added:

  • IEnumerable<MemberContext> GetFieldsForSerialization(): For processing fields one-by-one
  • IEnumerable<MemberContext> GetFieldsForDeserialization()
  • XElement Serialize(object obj, SerializerOptions options = null): All members of a type can be serialized without using the current ICustomSerializer or IKnownType
  • object Deserialize(XElement element, SerializerOptions options = null): All members of a type can be deserialized without using the current ICustomSerializer or IKnownType

@@ -0,0 +1,50 @@
// Copyright (C) Sina Iravanian, Julian Verdurmen, axuno gGmbH and other contributors.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is responsible for blocking recursive calls to ICustomSerializer orn IKnownType instances. This way the YAXSerializer can de/serialize a "raw version", which can then be modified.

@@ -0,0 +1,51 @@
// Copyright (C) Sina Iravanian, Julian Verdurmen, axuno gGmbH and other contributors.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the equivalent to the ITypeContext for members

if (!member.CanWrite)
return false;

if (member.IsAttributedAsDontSerialize)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to IEnumerable<MemberWrapper> GetFieldsToBeSerialized(bool sorted = true)

@@ -1551,22 +1549,4 @@ internal LoadOptions GetXmlLoadOptions()
options |= LoadOptions.SetLineInfo;
return options;
}

private static object InvokeCustomDeserializerFromElement(Type customDeserType, XElement elemToDeser, MemberWrapper memberWrapper, UdtWrapper udtWrapper, YAXSerializer currentSerializer)
{
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to CustomSerializerWrapper

@@ -1,49 +0,0 @@
// Copyright (C) Sina Iravanian, Julian Verdurmen, axuno gGmbH and other contributors.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to folder "Custom". Maybe there's a better name so summarize the content of the folder.
Namespace of files in there is still YAXLib-

@@ -3,11 +3,10 @@

#nullable enable
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored to use the new features of ISerializationContext

@@ -80,7 +80,7 @@ public MemberWrapper(MemberInfo memberInfo, SerializerOptions? serializerOptions
_isProperty = true;
}

Alias = StringUtils.RefineSingleElement(MemberInfo.Name);
_alias = Alias = StringUtils.RefineSingleElement(MemberInfo.Name);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks strange, but needed to initialize the backing field _alias.


/// <summary>
/// Gets a value indicating whether this instance has custom serializer.
/// </summary>
/// <value>
/// <c>true</c> if this instance has custom serializer; otherwise, <c>false</c>.
/// </value>
public bool HasCustomSerializer => CustomSerializerType != null;
public bool HasCustomSerializer => CustomSerializer != null;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the instance, not only the type.

var typeWrapper = UdtWrapperCache.Instance.GetOrAddItem(dstType, null);
convertedObj = typeWrapper.EnumWrapper.ParseAlias(value.ToString());
var typeWrapper = UdtWrapperCache.Instance.GetOrAddItem(dstType, DefaultSerializerOptions);
convertedObj = typeWrapper.EnumWrapper!.ParseAlias(value.ToString());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options must not be null

{
elementValue = null;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to UdtWrapper.GetFieldsToBeSerialized

@@ -1161,25 +1171,6 @@ private XElement MakeBaseElement(XElement insertionLocation, XName name, object
return elem;
}

private static void InvokeCustomSerializerToElement(Type customSerType, object objToSerialize, XElement elemToFill, MemberWrapper memberWrapper, UdtWrapper udtWrapper, YAXSerializer currentSerializer)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to CustomSerializerWrapper

@axunonb axunonb marked this pull request as ready for review August 18, 2022 20:18
@304NotModified 304NotModified self-assigned this Aug 19, 2022
@axunonb
Copy link
Collaborator Author

axunonb commented Aug 27, 2022

Got some space to have look?

Included types:
CustomSerializerWrapper, IMemberContext, ISerializationContext, ITypeContext, Locker, MemberContext, SerializationContext, TypeContext
@axunonb
Copy link
Collaborator Author

axunonb commented Sep 1, 2022

Got some space to have look?

@304NotModified Any chance? Its really messy to go on without this PR merged.

@axunonb axunonb merged commit 4f21807 into YAXLib:version/4.0 Sep 2, 2022
@axunonb axunonb deleted the pr-custom-serializer branch September 2, 2022 06:40
axunonb added a commit that referenced this pull request Oct 30, 2022
* Custom Serialization (#181)

* Ese assembly name for checking members for .NET origin (fixes #179) (#180)

* Implement YAXSerializationOptions.DontSerializeNullObjects for deserialization (#177)

Fixes #174

* Added a serialization context when invoking CustomSerializers (#167)

* Added a serialization context when invoking CustomSerializers
* Feature/initial step single responsibility principal (#168)
* Added regions to help make the YAXSerializer more readable.
* Single Responsibility Principal, moved logic related to creating a custom serializer to it's own class.
* Single Responsibility Principal, moved all or at least most logic related to namespaces to it's own class.
Co-authored-by: bob.sanders <bob.sanders@valtech.com>
* Fixes #166 ICustomSerializers may have an interface as the generic argument (#169)
e.g. public class SomeSerializer : ICustomSerializer<ISomeInterface> {
   //...
}
* Update to VS2022 and update NuGet key (#171)
* Update appveyor
* use build worker image "Visual Studio 2022"
* update for expiring NuGet API key
* Update sln to Visual Studio Version 17
* Type handling and related exceptions (#172)
* Fixes #164
* Updated appveyor.yml

* Added GitHub Action for SonarCloud integration (#173)

* Added GitHub Action for SonarCloud integration

* Coverage for line AND branch

* Add generic constructor (#170)

* add generic constructor
* add generic methods
* rename generic filename as requested

* Several updates to PR

* Rebase to latest master
* nullable enable for ISerialilzationContext
* ISerializationContext with new member SerializerOptions
* nullable enable for MemberWrapper
* separate file for SerializationContext
* Test for SerializationContext

* Fix possible null reference warnings

* Fix: No invokation of ICustomSerializer on deserialization

Bug: ICustomSerializer was only called after first deserialization

Reasons:
* In YAXSerializer, HasCustomSerializer was tested BEFORE analyzing the RealType attribute, to it was not called for deserialization
* At the same time, the default deserialization gave the correct result

Measures:
* HasCustomSerializer is tested AFTER analyzing any RealType attribute
* Tests will now fail, if ICustomSerializer is not called

Co-authored-by: prettyv <prettyvanilla@posteo.at>
Co-authored-by: bobduncan <bobsanders13@hotmail.com>

* Trigger SonarCloud also for 'version/**' branches (#182)

* Added IYAXSerializer and proper inheritance for YAXSerializer<T> (#183)

* Added IYAXSerializer and proper inheritance for YAXSerializer<T>

* Added generic test

Co-authored-by: Julian Verdurmen <304NotModified@users.noreply.github.com>

* Remove usage of obsolete constructors and members (#189)

Remove usage of obsolete constructors and members
* All YAXLibTests and DemoApplication updated
* Removed SerializerOptionsTests (containing no tests, but still covered 100%)

Small fixes from review
* Corrected blank + apply latest C# features
* Corrected indent, unused variables in SerializationTest replaced with discard _

* Remove obsolete YAXLib classes and methods  (#190)

Remove obsolete classes and methods

* YAXSerializer: Remove obsolete constructors and members
* Remove class YAXElementValueAlreadyExistsException
* Remove class YAXInvalidFormatProvided
* Correct TypeWrapper method name 'SetYAXSerializerOptions' to 'SetYAXSerializationOptions'

* All (de)serialize tests for the generic and non-generic version (#194)

All serialize tests for the generic and non-generic version

+ small cleanup

formatting

Co-authored-by: Julian Verdurmen <304NotModified@users.noreply.github.com>

* Improve maintainability: SonarCloud Cognitive Complexity <= 20 (#186)

* Fix: EnumWrapper(Type t) throws ArgumentException instead of wrong ArithmeticException
* YAXSerializer split into new internal classes Serialization and Deserialization with existing code (single responsibility)
* Create new methods to remove code duplications, keeping existing code
* Make private method and variable names more self-explanatory, corrected typos
* Remove unused arguments in private methods
* Merge adjacent if statements
* Remove redundant null checks, add missing null checks
* Update xmldoc containing references to non-existing classes/members
* Create internal IYaxMemberLevelAttribute: implemented in MemberWrapper and existing attributes for members
* Create internal IYaxTypeLevelAttribute: implemented in UdtWrapper and existing attributes for types
* Move code from MemberWrapper.ProcessYAXAttribute(object attr) to corresponding IYaxMemberLevelAttribute.Setup(...) of attributes
* Move code from UdtWrapper.ProcessYAXAttribute(object attr) to corresponding IYaxTypeLevelAttribute.Setup(...) of attributes
* Most properties with backing fields are now auto-properties in MemberWrapper and UdtWrapper
* Remove outdated "TODO" comments
* Remove comments containing old code
* Add setter to IRecursionCounter.RecursionCount
* 100% unit test coverage for YAXSerializer and YAXSerializer<T> public members

* Add option to serialize private members from base types (#192)

* Added option YAXSerializableTypeAttribute.IncludePrivateMembersFromBaseTypes
* Added extension method for Type,GetMembers(Type, BindingFlags, bool), used in YAXSerializer.GetFieldsToBeSerialized(UdtWrapper)
* Added property UdtWrapper.IncludePrivateMembersFromBaseTypes
* Added unit tests
* Modfied xmldoc with references to that option
* Fixed xmldoc in YAXSerializationOptions
* Closes #36

* Extend caching and use object pooling for better performance (#199)

* Added/extended Caching and Object Pooling

Main achievements:
* Increase speed by factor 5.5
* Descrease GC pressure by factor 5.5

* Created caches for UdtWrapper and MemberWrapper in new namespace Caching
* Added all unit tests for caching
* Created object pool in namespace Pooling. Using SerializerPool for YAXSerializer, StringBuilderPool for Stringbuilder
* Added all unit tests for pooling
* GenericDeserializationTests and GenericSerializationTests contains tests for all pool items are returned to pools after completing TestFixtures
* Added SetupFixture for PoolSettings.CheckReturnedObjectsExistInPool = true

Code clean-up / miscellaneous:
* extended furhter code sections with "nullable enable"
* Replaced throwing System.Exception by more specific exception types
* Fixed unit test where Assert arguments were mixed-up
* Updated Microsoft.SourceLink.GitHub to v1.1.1

* Use Stringbuilder from pool instead of string concatenation

* Added MaxCacheSize = DefaultCacheSize to MemberWrapperCache

* Changes from review comments

* Remove classes MruCache and MruCacheTests
* UdtWrapperCache.DefaultCacheSize = 500
* MemberWrapperCache.DefaultCacheSize = 10000
* SerializerOptions.DefaultMaxRecursion = 50;
* Remove property PoolSettings.IsPoolingEnabled
* Remove "magic zero" as "endless" for MaxRecursion. To achieve old behavior, set it to int.MaxValue

* Refactoring from review comments

* Move methods from YAXSerializer to UdtWrapper (#200)

* Refactor Known Types (#202)

** Known Type related **

* All KnownType<T>.Serialization(...) and KnownType<T>.Deserialization methods have SerializationContext as argument
* Refactor KnownTypeExtensions: It's now XNamespaceExtensions (IKnowType was not used)
* Move classes in folder KnownTypes to namespace KnownTypes
* Rename class KnownTypes to WellKnownTypes and add methods:
        Add(...)
        Remove(...)
        Clear()
        RestoreDefault()
        TryGetKnownType(Type? type, out IKnownType? knownType)
* Add KnownBaseTypeBase<T> as another kind of KnownType<T>
* Add ExceptionKnownBaseType as the first KnownBaseTypeBase<T>
* Add ReflectionUtils.IsBaseClassOrSubclassOf(Type subType, string baseName) to determine Exception subclasses
* Add methods SetFieldValue and SetPropertyValue to ReflectionUtils. They can read/write (also) to private members of base classes. Used for setting the Exception's private _message member.
* Make IKnownType, KnownTypeBase, DynamicKnownTypeBase, KnownBaseTypeBase and WellKnownTypes public
* Add RecursionCount as property to SerializationContext to keep recursion under control in IKnownTypes and ICustomSerializers.

** Other **

* YAXSerializationOptions now have flag "None = 0". SerializeNullObjects is still the default, and is set in the CTOR of YAXSerializer
* UdtWrapper CTOR takes SerializerOptions as argument, instead of YAXSerializer
* MemberWrapper CTOR takes SerializerOptions as argument, instead of YAXSerializer
* UdtWrapper and MemberWrapper have a property IKnownType? KnownType to avoid looking up known types repetitively
* Make abstract YAXException CTORs protected
* Increase test coverage for known types, ReflectionUtils, and overall test coverage
* Various code fixed red-flagged by SonarCloud
* Update xmldoc

* Refactor custom serialization and known type serialization (#204)

* Resolves #203

**** UdtWrapper ****

Make UdtWrapper.GetFieldsToBeSerialized private, add methods
* UdtWrapper.GetFieldsForSerialization()
* UdtWrapper.GetFieldsForDeserialization(bool)
* Move member-related checks, that are needed for serialization and deserialization from Serialization.SerializeFields(...) and Deserialization.DeserializeDefault(...) to UdtWrapper.GetFieldsToBeSerialized(...)

**** Folder "Custom" (namespace not yet aligned) ****

* ISerializationContext / SerializationContext
  - Refactor:  Members are ITypeContext, IMemberContext?, SerializerOptions, RecursionCount
  - ISerializationContext.ITypeContext will always be set
  - If a member is de/serialized IMemberContext will always be assigned for ICustomSerializer and IKnownType
* ITypeContext / TypeContext
  - Has methods to de/serialize the current type
  - Has methods GetFieldsForSerialization() and GetFieldsForDeserialization
* IMemberContext / MemberContext
  - Has members MemberInfo, FieldInfo?, PropertyInfo?, TypeContext
  - Has method GetValue()
* CustomSerializerWrapper
  - A wrapper for all ICustomSerializers
* Locker, used in Serialization, Deserialization, CustomSerializerWrapper
  - Blocks recursive calls to ICustomSerializers and IKnownTypes
* ICustomSerializer: Update xmldoc for changing element/attribute name
* Refactor Serialization.InvokeCustomSerializer: Code equivalent to Deserialization.InvokeCustomSerializer

**** ExceptionKnownBaseType ****

* Refactor using new features of SerializationContext

**** Invokation of ICustomSerializers ****

* Remove ReflectionBridgeExtensions
* Add CustomSerializerWrapper which includes invokation

**** Other ****

* UdtWrapper.Alias is not nullable
* MemberWrapper.Alias is not nullable
* XMLUtils.ToXmlValue handles null properly

**** Unit tests ****

* Add tests for new features
* Modified existing tests to use new features

* Create Namespace YAXLib.Customization

Included types:
CustomSerializerWrapper, IMemberContext, ISerializationContext, ITypeContext, Locker, MemberContext, SerializationContext, TypeContext

* Add NetStandard2.1 as target framework (#207)

* Add text embedding for XML elements, option to strip invalid XML chars (#208)

* Add NetStandard2.1 as target framework
* Add text embedding for XML elements, strip invalid XML chars
* Added YAXTextEmbeddingAttribute which allows for TextEmbedding.None, TextEmbedding.CData, TextEmbedding.Base64
* Added YAXSerializationOptions.StripInvalidXmlChars to remove all invalid characters from serialization output

* Refactor YAXLib for Nullable Reference Types (#209)

* NRT for YAXLib.Options
* NRT for YAXLib.Pooling
* NRT for YAXLib.KnownTypes
* NRT for YAXLib.Exceptions and ReflectionUtils
* NRT for YAXLib.Enums
* NRT for YAXLib.Customization
* NRT for YAXLib.Caching and YAXLib.Attributes
* NRT for EnumWrapper, StringExtensions, StringUtils, TypeExtensions
* NRT for core classes

YAXSerializer
YAXSerializerOfT
MemberWrapper
UdtWrapper
Serialization
Deserialization
YAXParsingErrors
XmlNamespaceManager
XMLUtils
IYAXSerializer
IRecursionCounter
ICustomSerializer

* Set global 'nullable enable' except YAXLibTests, Demo
* Removed #nullable enable preprocesser directive
* Directory.Build.props: added nullable enable
* YAXLibTests, DemoApplication: set nullable disable in *.csproj

* Integrate changes from PR #208 review

* TypeContext.Deserialize/.Serialize: options may be null (#210)

* Revert TypeContext.De/Serialize: options may be null again

See PR comment
#209 (comment)

* Update ITypeContext

* Bump version 4.0.0-pre.1 (#211)

* NRT enabled for YAXLibTests and DemoApplication (#212)

* csproj files modified with nullable enable
* Fixed possible null references
* YAXLibTests method/type names follow naming convention

* Reformat solution (#213)

Reformat solution
Does not contain any changes in code logic - automated with ReSharper
* Use file-scoped namespace
* Remove unused directives
* Reformat code and embedded xmldoc comments
* Correct typos in xmldoc comments
* Remove remaining (obsolete) "#nullable enable"
* Remove redundant this-qualifier
* Set file-scoped namespace declaration in .editorconfig

* Prepare to merge branch version/4.0 to master

* Update appveyor for version and branch
* Remove destructor from Locker

Co-authored-by: prettyv <prettyvanilla@posteo.at>
Co-authored-by: bobduncan <bobsanders13@hotmail.com>
Co-authored-by: Julian Verdurmen <5808377+304NotModified@users.noreply.github.com>
Co-authored-by: Julian Verdurmen <304NotModified@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants