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 SerializationResourceManager to use Dictionary instead of HashTable #8332

Merged

Conversation

elachlan
Copy link
Contributor

@elachlan elachlan commented Dec 6, 2022

  • Refactored ResourceCodeDomSerializer.SerializationResourceManager to replace usages of Hashtable.
  • Refactored ResXResourceReader to use Dictionary instead of ListDictionary

Related: #8143

Microsoft Reviewers: Open in CodeFlow

@elachlan elachlan requested a review from a team as a code owner December 6, 2022 01:27
@ghost ghost assigned elachlan Dec 6, 2022
@@ -314,7 +313,7 @@ public IDictionaryEnumerator GetEnumerator()
{
_isReaderDirty = true;
EnsureResData();
return _resData.GetEnumerator();
return ((IDictionary)_resData).GetEnumerator();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part was required, which might make the whole thing pointless if there is a performance cost for the cast?

Ultimately it would be nice if these were updated to be properly typed, but that is a breaking change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IDictionary is one of the last implemented interfaces, so performance won't be so great.
https://www.thomasclaudiushuber.com/2020/03/05/c-the-order-of-interfaces-is-important-for-casting-performance/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JeremyKuhne I'd love your input on this. The move to generics might be a performance regression here.

Copy link
Member

Choose a reason for hiding this comment

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

Casting is not going to measure in a scenario like this. While there is a difference, it is incredibly small. A few nanoseconds are not going to matter in something that won't run more than a few times.

}

return null;
Dictionary<string, object> ht = GetResourceSet(culture);
Copy link
Member

Choose a reason for hiding this comment

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

nullable declaration?

if (t is not null)
{
// This is for backwards compatibility and also for the case when our reader/writer don't support metadata. We must merge the original enumeration data in here or else existing design time properties won't show up. That would be really bad for things like Localizable.
Hashtable it = GetResourceSet(CultureInfo.InvariantCulture);
Dictionary<string, object> it = GetResourceSet(CultureInfo.InvariantCulture);
Copy link
Member

Choose a reason for hiding this comment

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

nullable?

@dreddy-work
Copy link
Member

@elachlan, I see partial nullability in these files. There are multiple instances that needs to be declared as nullable.

@dreddy-work dreddy-work added the 📭 waiting-author-feedback The team requires more information from the author label Dec 6, 2022
@ghost ghost removed the 📭 waiting-author-feedback The team requires more information from the author label Dec 7, 2022
@elachlan
Copy link
Contributor Author

elachlan commented Dec 7, 2022

@elachlan, I see partial nullability in these files. There are multiple instances that needs to be declared as nullable.

@dreddy-work System.Windows.Forms.Design does not have <Nullable>enable</Nullable> in its project file. So for this issue I'd like to avoid the nullability changes for now and come back to it in another PR.

Edit: I've created an issue for it at #8342

@dreddy-work
Copy link
Member

@elachlan , seems some left out causing build failures : src\System.Windows.Forms.Design\src\System\ComponentModel\Design\Serialization\ResourceCodeDomSerializer.SerializationResourceManager.cs(458,40): error CS8632: (NETCORE_ENGINEERING_TELEMETRY=Build) The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

@dreddy-work dreddy-work added the 📭 waiting-author-feedback The team requires more information from the author label Dec 7, 2022
@@ -874,23 +803,23 @@ public string SetValue(IDesignerSerializationManager manager, ExpressionContext
{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dreddy-work The for loop here has a super weird definition. There are two of these types of for loops within this code file. What would you suggest to do here?

Copy link
Member

Choose a reason for hiding this comment

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

Loop is trying to find unique name that wasn't used yet. We could simplify it to do-while for readability and should be able to remove warning. I do not see any other technical advantages though. Let's convert to do-while?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can write this into a Dictionary<string, int> and use the baseName as the key. Therefore keeping track of the count. I'll push through once I work out the proper logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dreddy-work done! Both should be easier to understand now I think.

@ghost ghost removed the 📭 waiting-author-feedback The team requires more information from the author label Dec 7, 2022
@dreddy-work dreddy-work added the 📭 waiting-author-feedback The team requires more information from the author label Dec 7, 2022
@ghost ghost removed the 📭 waiting-author-feedback The team requires more information from the author label Dec 7, 2022
{
return CompareValue.Different;
return !parentValue.Equals(value) || parentValue is null ? CompareValue.Different : CompareValue.Same;
Copy link
Member

Choose a reason for hiding this comment

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

what if both parentValue and value is null?

Copy link
Member

Choose a reason for hiding this comment

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

@elachlan , did you run any tests here? This is core for resource serialization and like to do some unit testing performed with this change and record results before we merge it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not do any manual testing, but did run the Design Unit Tests.

The ordering seemed important here. Because previously it was checking if both were equal before checking for null. Meaning that its designed to allow both to be null for Same to be returned. So I replicated that logic here.

Copy link
Member

Choose a reason for hiding this comment

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

can you try a simple application with multiple sets of controls and localize the application?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have no idea how to do this. You mean as a part of one of the test projects?

Copy link
Member

Choose a reason for hiding this comment

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

No, by replacing binaries locally on your machine and create .NET 8.0 targeted application. Here are instructions on private testing in case you have not used them before: https://github.com/dotnet/winforms/blob/main/docs/testing.md

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was playing around with the tests, the code is only hit in SerializationResourceManager when the PrimitiveCodeDomSerializer is serializing a string with a char count > 200. This isn't tested for.

I tried testing this scenario and writing a separate test class for ResourceCodeDomSerializer. Both resulted in exceptions due to either a missing session. If I manually created a session, then the exception would be on SerializationResourceManager.Writer with a missing ResourceWriter service.

If I could work out how to add a writer service in the test, then I can write tests for it.

Copy link
Member

Choose a reason for hiding this comment

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

good to know that. Logically, change is no-op here. Lets take it in after addressing rest of the comments.

@dreddy-work dreddy-work enabled auto-merge (squash) December 8, 2022 18:03
@dreddy-work dreddy-work merged commit f0d0562 into dotnet:main Dec 8, 2022
@ghost ghost added this to the 8.0 Preview1 milestone Dec 8, 2022
@elachlan elachlan deleted the SerializationResourceManager-HashTable branch December 8, 2022 21:33
@ghost ghost locked as resolved and limited conversation to collaborators Jan 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants