使用 get_Keys() 方法进行集合是否正确

iRo*_*Ron 4 .net collections methods powershell hashtable

与此问题相关:我可以将名为“keys”的键添加到哈希表而不覆盖“keys”成员吗? 实际上,我经常使用该get_Keys() 方法,因为推荐的PSBase 属性只会解决问题。
换句话说,对于未知的目录键列表,在解决方案如下的情况下,此方法显得特别方便:Powershell Merge 2列出 Performance

该方法按预期工作(也在其他集合、任何 PowerShell 版本和 .Net Core 上):

$Hash = @{Keys = 'MyKeys'; PSBase = 'MyPSBase'}
$Hash.get_Keys()
PSBase
Keys
Run Code Online (Sandbox Code Playgroud)

get_Keys()方法显然源自:

$Hash.get_Keys

OverloadDefinitions
-------------------
System.Collections.ICollection get_Keys()
System.Collections.ICollection IDictionary.get_Keys()
Run Code Online (Sandbox Code Playgroud)

关键是我无法找到在哪里找到对此get_Keys()方法的引用,并且几乎没有任何有关此方法的文档。
使用这种方法安全吗?

Mat*_*sen 5

get_Keys()确实是访问字典属性的有效(且推荐)方法,Keys不会冒与用户定义的键发生冲突的风险。

该方法按预期工作(也在其他集合、任何 PowerShell 版本和 .Net Core 上):

请注意,这仅适用于字典[hashtable][ordered][SortedList]) - 因为它是从接口继承System.Collections.IDictionary

公共文档中没有列出的原因get_Keys()是它是故意隐藏的

要理解其中的原因,我们首先需要了解.NET 中属性的本质

.NET 中的属性

在 .NET 中,数据类型可以有不同种类的成员。在下面的 (C#) 示例中,我们定义一个具有两个成员、一个字段和一个方法的类:

class MyClass
{
    int MyField;

    int MyMethod(string n = "")
    {
        return int.Parse(n);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意的有趣的事情是,该字段就像一个变量- 我们可以引用它来获取存储在其中的任何整数的值MyField,并且我们可以为其分配一个(新)值。另一方面,该方法的行为就像一个函数- 我们可以调用它,包括向它传递参数值,并且它可以返回一个值。

但是 .NET 有第三种成员类型,它充当字段和方法之间的混合体,它看起来有点像这样(在字典的情况下):

class MyDictionary
{
    string[] _keys;
    
    public string[] Keys
    {
        get
        {
            return _keys;
        }
        set
        {
            throw new InvalidOperationException("Don't mess with the keys!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这被称为属性- 从用户的角度来看,该Keys属性就像一个字段 - 我们可以引用它来解析它的值,并且我们可以(尝试)分配给它 - 但从实现者的角度来看,我们还有更多控制它的行为,比如能够(有条件地)在赋值时抛出异常。

现在,当编译上述代码时,C# 编译器需要将getset方法存储在某处,以便 CLR 知道在有人尝试Keys在运行时解析成员时执行它们。

约定是将它们生成为常规类方法,通过在相关属性名称前面添加get_和来命名。set_编译器进一步用SpecialName属性标志标记这些方法,允许编辑器和分析器将它们隐藏在用户界面和自动完成器中 - 这正是方法名称不会自动显示在智能感知等中的原因。

发现属性 getter/setter

*在 PowerShell 类中,成员始终是方法属性,因此在下面我将使用此 PowerShell 类定义作为示例:

class StackOverflowUser
{
  [string]$Name
  [int]$ID

  StackOverflowUser([string]$name, [int]$id)
  {
    $this.Name = $name
    $this.ID   = $ID
  }
}

$Mathias = [StackOverflowUser]::new("Mathias R. Jessen", 712649)
Run Code Online (Sandbox Code Playgroud)

使用Get-Member

您可以使用以下方法发现与属性关联的自动 getter 和 setter 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 )
Run Code Online (Sandbox Code Playgroud)

$ID在这里我们可以看到与和相关的 getter 和 setter 方法$Name

使用反射

我们还可以直接从[type]对象中找到这些:

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           : {}
Run Code Online (Sandbox Code Playgroud)

请注意,上面的 getter 具有SpecialName上面讨论的属性

注意:上面的输出来自 PowerShell 7 ,由于 .NET Core 中反射/类型系统 API 的更改,在 Windows PowerShell 中会略有不同

我希望这能解释:)