-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Move PSVersionInfo.PSVersionName to first place #3562
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
Conversation
Quoted from @rkeithhill's comment
|
Ok, tomorrow. |
8c116eb
to
6dd8c70
Compare
keyList.Sort(); | ||
MoveItemOnFirstPlace(PSVersionInfo.PSEditionName, keyList); | ||
MoveItemOnFirstPlace(PSVersionInfo.PSVersionName, keyList); | ||
return keyList.ToArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation is a little bit heavy, it involves shuffling keyList's elements for 3 times (sort()
and 2 MoveItemOnFirstPlace
) + 2 collection allocation (ArrayList
and ToArray
).
Maybe the following pseudo code would be better?
Array arr = new string[base.Keys.Count];
int index = 2;
foreach (string key in base.Keys)
{
switch (key)
case PSVersionInfo.PSVersionName:
arr[0] = key;
break;
case PSVersionInfo.PSEditionName:
arr[1] = key;
break;
default:
arr[index++] = key;
break;
}
Array.Sort(arr, 2, arr.Length - 2; /* the equalityComparer passed in the constructor */);
return arr;
It allocate only one collection, and elements in arr
only get moved around once, within the Sort
method.
The pseudo code is still not perfect, because we will create an array object every time Keys
is accessed. If we want to go further to avoid that, we can create our own type that implements ICollection
and is immutable from outside (no public members to mutate it, just like the type of object that Hashtable.Keys
returns -- Hashtable+KeyCollection
). Then we can keep one instance of it with PSVersionHashTable
, and always reuse and return the same instance from Keys
property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for help with the code!
I added the optimized sorting and description.
If we do Keys
read-only we should do PSVersionTable
read-only too. And it is a breaking change - currently we can add in PSVersionTable
own version strings. For example, third-party module can add its strings in PSVersionTable
. I think it's not worth and we can live without the Keys
cache. What are your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PSVersionTable
doesn't need to be read-only for the Keys
to be read-only. Hashtable.Keys
returns an object of System.Collections.Hashtable+KeyCollection
, which expose no public members for you to mutate it.
This would be a pure optimization and is not required for this fix.
@@ -57,7 +59,7 @@ static PSVersionInfo() | |||
s_psVersionTable = new PSVersionHashTable(StringComparer.OrdinalIgnoreCase); | |||
|
|||
s_psVersionTable[PSVersionInfo.PSVersionName] = s_psV6Version; | |||
s_psVersionTable["PSEdition"] = PSEditionValue; | |||
s_psVersionTable[PSVersionInfo.PSEditionName] = PSEditionValue; | |||
s_psVersionTable["BuildVersion"] = GetBuildVersion(); | |||
s_psVersionTable["GitCommitId"] = GetCommitInfo(); | |||
s_psVersionTable["PSCompatibleVersions"] = new Version[] { s_psV1Version, s_psV2Version, s_psV3Version, s_psV4Version, s_psV5Version, s_psV51Version, s_psV6Version }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not have const variables for other keys: "BuildVersion", "GitCommitId" and "PSCompatibleVersions"? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not want to distract the unrelated changes. But I agree to bring it into line if you don't mind.
@iSazonov, the implementation will break in 2 cases:
I pushed a fix to your branch, and need your help to review the code to see if there are other issues. |
Why we want to allow non-string keys? It seems the keys in |
And maybe move our logic in custom StringComparer? |
We don't want non-string keys, but user can add any key-value pairs to |
If we are faced with the keys of arbitrary type we would use In last commit I also have tried to move sorting logic to custom Comparer. It looks more simple 😄 If you dont agree feel free to revert. |
public override int Compare(object x, object y) | ||
{ | ||
string xString = (string)LanguagePrimitives.ConvertTo(x, typeof(string), null); | ||
string yString = (string)LanguagePrimitives.ConvertTo(y, typeof(string), null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if x
or y
are objects but calling LanguagePrimitives.ConvertTo
on them returns PSVersion
or PSEdition
as the converted string?
I still prefer to do sorting only if keys are all strings. If there is a non-string key, we just give up sorting and return base.Keys
.
Sorry for being picky with rare cases. Since we are creating a public accessible data structure, we have to consider whatever that might happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very useful for me. 😄 The more we did not analyse this deeply before.
All previous options that we had, nothing broke really. The only thing we do here is to change a human output to the screen.
Output (Out-Default) always perform conversion to strings. Even if we have non-string key, it will be converted to a string. And it seems better to even see them sorted.
If a user adds a record with key that is converted to "PSVersion" we see two entries. We could block it but it will be breaking change.
Also we should use LanguagePrimitives.ConvertTo<string>(Value)
(#2678 (comment)) to avoid "strange" sorting.
$a=[pscustomobject]@{v1=123}
$a
$a.ToString() # It is empty!
$PSVersionTable[$a]="asdfgh"
$PSVersionTable
Name Value
---- -----
PSVersion 6.0.0-alpha
PSEdition Core
@{v1=123} asdfgh
BuildVersion 3.0.0.0
CLRVersion
GitCommitId v6.0.0-alpha.18-36-gc367cb58d87a84597077fbbe9a5ded5310179878
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
$b=$a | Add-Member -MemberType ScriptMethod -Name ToString -Value {"PSVersion"} -PassThru -Force
$PSVersionTable[$b]="zxcvbn"
$PSVersionTable
Name Value
---- -----
PSVersion zxcvbn
PSVersion 6.0.0-alpha
PSEdition Core
@{v1=123} asdfgh
BuildVersion 3.0.0.0
CLRVersion
GitCommitId v6.0.0-alpha.18-36-gc367cb58d87a84597077fbbe9a5ded5310179878
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@daxian-dbw Could you please continue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry @iSazonov, I was busy with some other tasks. I will get back to this one ASAP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good argument. I agree to have the PSVersionTableComparer
for the sorting.
} | ||
} | ||
|
||
private class PSVersionTableComparer : Comparer<object> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just implement the IComparer
interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For PSVersionTable? We only compare keys not PSVersionTables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, for PSVersionTableComparer
. ArrayList.Sort(IComparer)
takes an IComparer
as argument.
{ | ||
public override int Compare(object x, object y) | ||
{ | ||
string xString = (string)LanguagePrimitives.ConvertTo(x, typeof(string), null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use CultureInfo.CurrentCulture
for the formatProvider
parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
{ | ||
string xString = (string)LanguagePrimitives.ConvertTo(x, typeof(string), null); | ||
string yString = (string)LanguagePrimitives.ConvertTo(y, typeof(string), null); | ||
if (PSVersionInfo.PSVersionName == xString) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of ==
, we should use PSVersionInfo.PSVersionName.Equals(xString, StringComparison.OrdinalIgnoreCase)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
} | ||
else | ||
{ | ||
return xString.CompareTo(yString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use String.Compare(string strA, string strB, System.StringComparison comparisonType)
so that you can specify StringComparison.OrdinalIgnoreCase
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
#3654 made changes to |
Array.Sort(arr, StringComparer.OrdinalIgnoreCase); | ||
return arr; | ||
ArrayList keyList = new ArrayList(base.Keys); | ||
keyList.Sort(new PSVersionTableComparer()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PSVersionHashTable
can have a static readonly field to hold an instance of PSVersionTableComparer
, so as to avoid creating a new comparer every time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@daxian-dbw Conflicts was resolved. Also I moved tests to PSVersionTable.Tests.ps1 file. |
} | ||
} | ||
|
||
private static PSVersionTableComparer keysComparer = new PSVersionTableComparer(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make it readonly
.
It's recommended to put field declarations before constructors, and for a private static field, it should be named with the prfix s_
-- s_keysComparer
. See s_psVersionTable
in this file for an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready.
} | ||
|
||
private static PSVersionTableComparer keysComparer = new PSVersionTableComparer(); | ||
private class PSVersionTableComparer : Comparer<object> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like we discussed, PSVersionTableComparer
can just implement IComparer
due to ArrayList.Sort(IComparer comparer)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -5,6 +5,7 @@ | |||
using System.Diagnostics; | |||
using System.Reflection; | |||
using System.Collections; | |||
using System.Collections.Generic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once you make PSVersionTableComparer
implements IComparer
instead of deriving from Comparer<object>
, this using directive can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
@daxian-dbw Thanks for review and usefull comments! |
@iSazonov Thanks for the great fix! 👍 |
Based on discussion #3530 (review)
We want
PSVersion
on first place and the rest sorted alphabetically