以下代码创建一个 C# 记录类型,使用哈希表对象初始化习惯用法将其实例化,然后修改记录的字段:
Add-Type -TypeDefinition @'
namespace n {
public record r {
public string s {get; init;}
}
}
'@
$r = [n.r]@{s='value'}
$r
$r.s = 'new_value'
$r
字段
s
表面上在初始化后是只读的,但输出如下:
s
-
value
new_value
using System;
namespace n {
public record r {
public string s {get; init;}
}
}
public class Program
{
public static void Main()
{
var r = new n.r {s="value"};
r.s = "new_value";
}
}
确实在编译时强制执行错误的不变性
Compilation error (line 13, col 3): Init-only property or indexer 'r.s' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.
我正在寻找一种方法来创建具有以下功能的类型:
[n.r]@{s='value'}
或类似的)来创建对象Add-Type -ReferenceAssemblies
可以创建这样的类型吗?
下面的代码尝试使用我能想到的在 C# 中创建这样一个不可变记录类型的各种方法来完成此任务。 (我不是 C# 专家,所以我希望我可能错过了一些。)作为参考,它也尝试使用
New-Object -Property
进行相同的操作。 它输出以下内容:
OK cl init_error init_origi init_mod_error init_after new_error new_origin new_mod_error new_after_
as nal _mod al mod
s
-- -- ---------- ---------- -------------- ---------- --------- ---------- ------------- ----------
X r1 value new_value value new_value
X r2 value new_value value new_value
X r3 value new_value value new_value
X r4 value new_value value new_value
X r5 Cannot create object of type … The property 's' cannot be fo… The value supplied is not val… The property 's' cannot be fo…
X r6 Cannot convert the "System.Co… The property 's' cannot be fo… A constructor was not found. … The property 's' cannot be fo…
X r7 Cannot convert the "System.Co… The property 's' cannot be fo… A constructor was not found. … The property 's' cannot be fo…
这些方法要么不支持哈希表对象初始化,要么允许在创建后修改对象属性。
Add-Type -TypeDefinition @'
namespace n {
public record r1 { public string s {get; init;} }
public record class r2 { public string s {get; init;} }
public record struct r3 { public string s {get; init;} }
public class r4 { public string s {get; init;} }
public class r5 { public string s {get;} }
public class r6 {
public readonly string s;
public r6 (string s) { this.s = s; }
}
public class r7 {
public string s {get;}
public r7 (string s) { this.s = s; }
}
}
'@
$(foreach ($n in 'r1','r2','r3','r4','r5','r6','r7') {
$r = $null
[pscustomobject]@{
class = $n
init_error = $(try { $r = Invoke-Expression "[n.$n]@{s='value'}"}
catch { $_ })
init_original = $r.s
init_mod_error = $(try { $r.s = 'new_value'}
catch { $_ } )
init_after_mod = $r.s
new_error = $(try { $r = New-Object "n.$n" -Property @{s='value'}}
catch {$_})
new_original = $r.s
new_mod_error = $(try { $r.s = 'new_value'}
catch { $_ } )
new_after_mod = $r.s
}
}) |
Select-Object `
-Property @{name='OK';
expression = {if ('value' -in $_.new_after_mod,
$_.init_after_mod ) {'✓'}
else {'X'}}},
* |
Format-Table `
-Property @{e='OK' ; width = 2},
@{e='class' ; width = 2},
@{e='init_error' ; width = 30},
@{e='init_original' ; width = 10},
@{e='init_mod_error' ; width = 30},
@{e='init_after_mod' ; width = 10},
@{e='new_error' ; width = 30},
@{e='new_original' ; width = 10},
@{e='new_mod_error' ; width = 30},
@{e='new_after_mod' ; width = 10}
PowerShell 会调用带有参数
System.Collections.Hashtable
的构造函数。 使用只读属性指定这样的构造函数似乎可以实现普通旧 C# 对象的只读属性的简洁命名初始化。
代码
Add-Type -TypeDefinition @'
namespace n {
public class r {
public string s {get;}
public r(System.Collections.Hashtable h) {
if (h.ContainsKey("s") &&
(null != (h["s"]))) {
this.s = h["s"] as string;
}
}
}
}
'@
$r = [n.r]@{s='value'}
$r
$r.s = 'new_value'
$r
输出
s
-
value
InvalidOperation:
Line |
18 | $r.s = 'new_value'
| ~~~~~~~~~~~~~~~~~~
| 's' is a ReadOnly property.
value