问题
Related to this question: Can I add a key named 'keys' to a hashtable without overriding the 'keys' member
, I am actually often using the get_Keys()
method as the recommended PSBase
property will just move the problem.
In other words, this method appears especially handy for an unknown list of directory keys in case of a solution like: Powershell Merge 2 lists Performance.
The method works as expected (also on other collections, any PowerShell version and .Net Core):
$Hash = @{Keys = 'MyKeys'; PSBase = 'MyPSBase'}
$Hash.get_Keys()
PSBase
Keys
The get_Keys()
method is apparently derived from:
$Hash.get_Keys
OverloadDefinitions
-------------------
System.Collections.ICollection get_Keys()
System.Collections.ICollection IDictionary.get_Keys()
The point is that I can't find back where I found the referral to this get_Keys()
method and there is hardly any documentation about this method.
Is it safe to use this method?
回答1:
get_Keys()
is indeed a valid (and recommended) way to access the Keys property of a dictionary without risking collision with a user-defined key.
The method works as expected (also on other collections, any PowerShell version and .Net Core):
Please beware that this only works on dictionaries ([hashtable]
, [ordered]
, [SortedList]
etc.) - as it's inherited from the System.Collections.IDictionary interface.
The reason get_Keys()
isn't listed in the public documentation is that it's intentionally hidden.
To understand why, we first need to understand the nature of properties in .NET
Properties in .NET
In .NET, data types can have different kinds of members. In the (C#) example below we define a class with two members, a field and a method:
class MyClass
{
int MyField;
int MyMethod(string n = "")
{
return int.Parse(n);
}
}
The interesting thing to notice here is that the field acts like a variable - we can reference it to obtain the value of whatever integer is stored in MyField
and we can assign a (new) value to it. The method, on the other hand, acts like a function - we can call it, including passing parameter values to it, and it can return a value.
But .NET has a third kind of member type that acts as a bit of a hybrid between a field and a method, and it looks a bit like this (in the case of a dictionary):
class MyDictionary
{
string[] Keys
{
string[] _keys;
get
{
return _keys;
}
set
{
throw new InvalidOperationException("Don't mess with the keys!");
}
}
}
This is known as a property - from a user perspective, the Keys
property will act just like a field - we can reference it to resolve its value and we can (attempt to) assign to it - but from an implementer's perspective we have a lot more control over it's behavior, like being able to (conditionally) throw an exception on assignment.
Now, when the above code is compiled, the C# compiler needs to store the get
and set
methods somewhere so that the CLR knows to execute them when someone tries to resolve the Keys
member at runtime.
The convention is to generate them as regular class methods, named by prepending get_
and set_
to the property name in question. The compiler further marks these methods with the SpecialName attribute flag, allowing editors and analyzers to hide them in user interfaces and auto-completers - which is exactly why the method name doesn't automatically show up in intellisense and the likes.
Discovering property getters/setters
*In PowerShell classes, members are always either methods or properties, so in the following I'll use this PowerShell class definition for the examples:
class StackOverflowUser
{
[string]$Name
[int]$ID
StackOverflowUser([string]$name, [int]$id)
{
$this.Name = $name
$this.ID = $ID
}
}
$Mathias = [StackOverflowUser]::new("Mathias R. Jessen", 712649)
Using Get-Member
You can discover the automatic getters and setters associated with a property using Get-Member -Force
:
PS C:\> $Mathias |Get-Member ?et_* -Force
TypeName: StackOverflowUser
Name MemberType Definition
---- ---------- ----------
get_ID Method int get_ID()
get_Name Method string get_Name()
set_ID Method void set_ID(int )
set_Name Method void set_Name(string )
Here we can see the getter and setter methods associated with $ID
and $Name
.
Using reflection
We can also find these directly from a [type]
object:
PS C:\> $IDPropertyInfo = [StackOverflowUser].GetProperty("ID")
PS C:\> $IDPropertyInfo.GetMethod
Name : get_ID
DeclaringType : StackOverflowUser
ReflectedType : StackOverflowUser
MemberType : Method
MetadataToken : 100663299
Module : RefEmit_InMemoryManifestModule
IsSecurityCritical : True
IsSecuritySafeCritical : False
IsSecurityTransparent : False
MethodHandle : System.RuntimeMethodHandle
Attributes : PrivateScope, Public, HideBySig, SpecialName
CallingConvention : Standard, HasThis
ReturnType : System.Int32
ReturnTypeCustomAttributes : Int32
ReturnParameter : Int32
IsCollectible : True
IsGenericMethod : False
IsGenericMethodDefinition : False
ContainsGenericParameters : False
MethodImplementationFlags : Managed
IsAbstract : False
IsConstructor : False
IsFinal : False
IsHideBySig : True
IsSpecialName : True
IsStatic : False
IsVirtual : False
IsAssembly : False
IsFamily : False
IsFamilyAndAssembly : False
IsFamilyOrAssembly : False
IsPrivate : False
IsPublic : True
IsConstructedGenericMethod : False
CustomAttributes : {}
Notice that the getter above has the SpecialName
attribute as discussed above
Note: output above is from PowerShell 7 and will be slightly different in Windows PowerShell due to changes in the reflection/type system APIs in .NET Core
I hope this explains :)
来源:https://stackoverflow.com/questions/60810458/is-it-correct-to-use-the-get-keys-method-for-collections